Thread 인자에 reference 쓰지 마라

STL 객체를 reference 로 넘기면 thread constructor 구체화에서 에러가 나버린다. 포인터로 넘기도록 하자.

condition_variable wait()

wait() 에 lock 만 입력하면? signal 을 받아도 기다린다?

    printf("Consumer : Waiting for signals..\n");
    unique_lock<mutex> uniq_lock(*lock);
    cond->wait(uniq_lock); // 이럼 그냥 기다린다?
    // cond->wait(uniq_lock, [] { return true; }); // 무조건 깨어나게 해야 한다?
    uniq_lock.unlock();

    printf("Consumer : Ok, I got it!\n");

cppreference.com 을 보면, 무조건 깬다고 하는데… 실제로도 디버깅을 해 보면 wait() 다음 구문에 stack frame pointer 가 잡혀있다. unique_lock 도 내 것으로 잡혀있다고 하는데, 그런데 왜 실행을 안 하는 건지?

진실

실행을 안 한 게 아니고.. **wait() 이전에 notify_one() 한건 효과가 없다. **producer 에서 다시 보내면 잘 받는다.

위의 lambda function 은 문제가 있는게, cpprefernce 에서도 이야기하지만 두 번째 인자는 이렇게 해석되기 때문이다. 즉, while 문 자체가 그냥 안 돌았단 뜻이다.

while (!Pred()) {
    wait(Lock);
}

condition_variable 이 무슨 queue 도 아니고… 그렇다.

굳이 wait 에 timeout 을 주고자 한다면, wait_for (timeout) 이나 wait_until (특정 시각) 을 써야 한다.

notify_one

짧게 적고 넘어가면, notify_one() 은 lock 이 필요없다. 필요가 없단 이야기는, lock 을 잡고 던져도 되고 안 잡고 던져도 된단 뜻이다. 중요한 건, 인자로 unique_lock 을 요구하지 않는다는 사실만 알면 된다.

둘 모두 차이가 있는데,

unique_lock 은 처음부터 lock 을 잡나

결론은 그렇다. unique_lock wrapper class 의 생성자를 보면 뒤에 옵션을 넣을 수 있는데, 옵션이 없으면 무조건 공급된 mutex 에 lock 을 잡고 시작한다.

그래서 누가 먼저 lock 을 잡고 있으면, unique_lock 생성자에서 block 된다. cond->wait 이전에 lock 을 반드시 잡아야 하는데, unique_lock 은 만들면서 잡기 때문에 명시적으로 lock() 함수 호출이 없는 것이다.

Lambda Function

람다(λ) 함수는 알아두면 좋을 것 같아 따로 정리한다. 느낌은 인라인 함수같겠지만, 좀 다르다.

    int my_mod = 7;
    cout << [my_mod](int _v) -> int { return _v % my_mod; }(15) << endl;

처음부터 introducer, parameters, return type, statement 다.

위의 예제를 빗대 설명하면

인자나 외부 참조가 없는 람다 함수도 된다. 이건 단순히 foo 를 출력한다. 여기서 반환형도 없으면 -> 도 안 쓰는걸 알 수 있다.

[]() { cout << "foo" << endl; }()

람다 함수는 런타임에 클로저(Closure) 객체로, 메모리에 임시로 존재하게 된다. 그냥 함수 객체가 되는 것이다.

Lambda Function 의 외부 캡처

여기서 말하는 외부 모든 변수는, 해당 람다 함수가 호출되는 스택에서의 인자와 지역 변수 까지다.

int glob_num = 3;
void main()
{
    int my_mod = 7;
    [&]() { cout << my_mod << endl; }();
    [&]() { cout << glob_num << endl; }();
    {
        int inner_mod = 9;
        [&]() { cout << inner_mod << endl; }();
        [&]() { cout << my_mod << endl; }();
        [&]() { cout << glob_num << endl; }();
    }
}

위의 코드는 모두 성공한다, 당연하다.

int glob_num = 3;
void myFunc() {
    int local_mod = 11;
    []() { cout << local_mod << endl; }(); // error!
    [&]() { cout << my_mod << endl; }(); // error!
    []() { cout << glob_num << endl; }();
}
void main()
{
    int my_mod = 7;
    // ...

여기서 두 차이가 생기는데, 둘 다 컴파일 에러다.