반응형
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
#include <cstdio>
#include <thread>
#include <mutex>
#include <queue>
 
using namespace std;
 
condition_variable signal;
mutex myMutex;
 
queue<int> q;
int name = 1;
int turn;
void func1() {
    int val = 1;
    while (1) {
        unique_lock<mutex> lock(myMutex);
        signal.wait(lock);
        if (q.empty())
        {
            cout << "Producer :: " << val << endl;
            q.push(val++);
        }
    }
}
void func2() {
    int myName;
    {
        unique_lock<mutex> lock(myMutex);
        myName = name++;
        cout << " myNmae :: " << myName << endl;
    }
    while (1) {
        unique_lock<mutex> lock(myMutex);
        signal.wait(lock);
        if (!q.empty() && turn == myName - 1)
        {
            cout << "Consumer " << myName << " :: " << q.front() << endl;
            q.pop();
            turn = (turn + 1) % 4;
        }
    }
}
int main()
{
    thread t1(func1); // &func1로 인자를 보내도 된다.
    thread t2(func2);
    thread t3(func2);
    thread t4(func2);
    thread t5(func2);
 
    while (1) {
        signal.notify_one();
        _sleep(30);
    }
    return 0;
}
 
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus


unique_lock을 이용하여 lock을 거는 방법이다.


unique_lock의 특성은 중괄호의 끝 '}'이 나타나기 전까지 lock을 걸어주고 중괄호가 끝나면 unlock을 자동으로 해준다.

unique_lock<mutex> lock(myMutex);에서

myMutex라는 뮤텍스를 기준으로 lock과 unlock을 행해주게 된다.

unique_lock<mutex> lock 변수를 이용해서 signal.wait(lock)을 하게되면 잠들게 되는 과정과 같아진다.

이때 메인 스레드에서 signal.notify_one()을 0.03초마다 해주게되면 잠들었던 스레드가 깨어나서 일을하게 된다.

이때 signal.wait()는 큐처럼 FIFO형식이라 먼저 들어온 스레드가 가장 먼저 깨어나게 된다.


따라서 t1 스레드가 queue에 값을 넣어주면 t2,t3,t4,t5가 차례대로 값을 pop하면서 Producer이 만들어내는것을 Consumer이 순차적으로 챙겨 갈 수 있다.


하지만 컨텍스트 스위칭의 시간이 각 스레드마다 불규칙적이므로 turn이라는 전역 변수를 이용하여 스레드를 규칙적으로 움직이도록 만들어준다.


이때 turn은 전역변수라 unsafe region이지만, turn은 lock이 걸린 부분에서 증가 연산이 일어나므로 safe하다.


마지막으로 name을 설정하는곳에도 unique_lock을 걸게 되는데 그 이유는 자신의 이름을 받는 도중 혹은 myName을 출력하는 도중에 컨텍스트 스위칭이 일어나면 불규칙적인 출력 혹은 name 설정이 잘못 될 수 있기 때문에 lock을 걸어준다.





반응형