Thread

#include <thread>
void worker_func(int a, const char* b) { .. }

int main() {
    thread t1(worker_func, 1, "Hello");
    t1.join(); // join
    t1.detach(); // or detach

    return 0;
}

끝이다. #include <thread> 하고 함수와 인자 넣은 thread 변수 만들고 join 또는 detach 하면 된다. 와!

#include <cstdio> // printf 쓰기 위해서
void worker_func(int a, const char* b) {
    thread::id this_id = this_thread::get_id();
    printf("thread %x", this_id);

thread::id 타입으로 현재 쓰레드 ID 를 받아올 수 있다. 받아오는 함수는 this_thread::get_id() 이다.

위에서 printf() 를 쓴 이유는, cout 을 쓸 경우 각 연산이 모두 병렬로 수행되어서 한 줄이 뒤섞여 나오기 때문이다. 여기서 말하는 각 연산은 바로 << 를 말한다.

int main() {
  vector<thread> workers;
  for (int i = 0; i < 4; i++) {
    workers.push_back(thread(worker));
  }

  for (int i = 0; i < 4; i++) {
    workers[i].join();
  }
  return 0;
}

main 에서는 보통 vector<thread> 를 이용해서 이렇게 관리한다. STL 에서 다루겠지만 push_back 으로 thread 원소를 추가한다.

mutex

#include <mutex>
mutex m;
m.lock();
m.unlock();

lock_guard

만약 unlock() 을 못하면 어떡하죠? state 관리를 해야 할까요? 다음 코드를 보자.

{
  lock_guard<mutex> lock(m);
  // do something
  // oh no! you forgot to unlock!
}

lock_guard 가 뮤텍스를 받아 객체를 만들었다. (이름이 공교롭게 lock..) 만들면서 자동으로 해당 뮤텍스의 lock 을 요청한다. 자, 그리고 중괄호가 끝나면 이 지역 객체는 소멸자를 호출한다.

아, 이 때 unlock() 이 자동 호출된다. (C# 의 using 구문과는 비슷해 보이지만 개념 상으론 많이 다르다.)

condition_variable

이건 좀.. 적응이 필요하다.

#include <condition_variable>

condition_variable * cv = aCondVariable;
cv->notify_one(); // cond_signal

std::unique_lock<mutex> lk(*m);
cv->wait( lk, [&] { .. }); // cond_wait.. 그런데 이거 안에 뭐야 함수야?

cv->notify_all(); // cond_signal 을 모든 thread 에 뿌리기

참고로, unique_lock<mutex>lock_guard<mutex> 와 비슷하다. 차이점이라면, unique_lock 은 unlock 이후 다시 lock 이 가능하다는 정도? 그래서 cv->wait()unique_lock 을 요구한다. 내부에서 다시 lock 을 잡아야 하기 때문에.

cond_wait 를 C에서 사용해보면 알겠지만, 반드시 들어가기 전에 lock 을 해야 한다. (안에서 풀었다 잡기 때문에) 여기서도 마찬가지로 내부 wait() 에서 unlock 부터 하기 때문에 반드시 lock 부터 잡고 들어가야 한다.

notify_one() 은 자고 있는 쓰레드 중 하나를 깨우는 것이다. 어느 녀석이 깨어날 지는 모르겠지만. 어차피 같은 일 하는 쓰레드니까 상관 없다 이건가.