(C++ : Concurrency) 8. condition variable

Posted by : at

Category : Cpp


#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
using namespace std::literals;

std::mutex m;
int shared_data = 0;

void consumer()
{
    // 읽는다
    std::lock_guard<std::mutex> lg(m);
    std::cout << "consume : " << shared_data << std::endl;
}

void producer()
{
    // 쓴다
    std::lockguard<std::mutex> lg(m);
    shared_data = 100;
    std::cout << "produce : " << shared_data << std::endl;
}

int main()
{
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
}

// 만약 쓰기전에 읽는 경우가 발생한다면??
// 데이터가 써지고 읽게 만들어보자
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#include <condition_variable>
using namespace std::literals;

std::mutex m;
std::condition_variable cv;
int shared_data = 0;

void consumer()
{
    // std::lock_guard<std::mutex> lg(m);
    std::unique_lock<std::mutex> ul(m); // condition variable은 unique_lock만 지원됨
    cv.wait(ul);    // wait에 들어가면 lock을 풀고 대기, 신호가 오면 다시 lock을 걸고 아래라인으로 넘어간다.
    std::cout << "consume : " << shared_data << std::endl;
}

void producer()
{
    {
        std::lockguard<std::mutex> lg(m);
        shared_data = 100;
        std::cout << "produce : " << shared_data << std::endl;
        // 이건 하나의 팁인데 lock을 풀고 notify_one을 주는게 신호를 바로 받을수 있어서 속도에서 좋다
    }
    cv.notify_one();
}

int main()
{
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
}

또 다른 문제

void consumer()
{
    std::this_thread::sleep_for(200ms);
    std::unique_lock<std::mutex> ul(m);
    cv.wait(ul);        // 2. notify를 기다리게 되면서 무한정 기다리게 된다.
    std::cout << "consume : " << shared_data << std::endl;
}

void producer()
{
    {
        std::lockguard<std::mutex> lg(m);
        shared_data = 100;
        std::cout << "produce : " << shared_data << std::endl;
    }
    cv.notify_one();    // 1. notify를 먼저주고
}

해결해 보자

std::mutex m;
std::condition_variable cv;
bool data_ready = false;
int shared_data = 0;

//...

void consumer()
{
    std::this_thread::sleep_for(200ms);
    std::unique_lock<std::mutex> ul(m);
    cv.wait(ul, []() { return data_ready; });
    std::cout << "consume : " << shared_data << std::endl;
}

void producer()
{
    {
        std::lockguard<std::mutex> lg(m);
        shared_data = 100;
        data_ready = true;
        std::cout << "produce : " << shared_data << std::endl;
    }
    cv.notify_one();
}

좀 더 발전시켜보자

std::mutex m;
std::condition_variable cv;
bool data_ready = false;
bool data_process = false;
int shared_data = 0;

//...

void consumer()
{
    std::unique_lock<std::mutex> ul(m);
    cv.wait(ul, []() { return data_ready; });
    std::cout << "consume : " << shared_data << std::endl;

    // 데이터가 잘 들어왔음을 알린다
    data_process = true;
    ul.unlock();
    cv.notify_one();
}

void producer()
{
    {
        std::lockguard<std::mutex> lg(m);
        shared_data = 100;
        data_ready = true;
        std::cout << "produce : " << shared_data << std::endl;
    }
    cv.notify_one();

    std::unique_lock<std::mutex> ul(m);
    cv.wait(ul, [](){return data_process;});
}

About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links