CS/C++

[Mastering C++ Programming] - Condition variable

sliver__ 2022. 12. 9. 15:59
728x90
  • 조건 변수 동기화 프리미티브는 둘 이상의 스레드가 서로 통신하고 특정 신호 또는 이벤트를 수신할 때만 진행해야 하는 경우에 사용됩니다. 
  • 특정 신호나 이벤트를 기다리는 스레드는 신호나 이벤트를 기다리기 전에 뮤텍스를 획득해야 합니다.

  • 생산자/소비자 문제가 있는 조건 변수의 사용 사례를 이해해 봅시다. 
  • PRODUCER와 CONSUMER라는 두 개의 스레드를 생성하겠습니다. 
  • PRODUCER 스레드는 대기열에 값을 추가하고 CONSUMER 스레드에 알립니다. 
  • CONSUMER 스레드는 Producer의 알림을 기다립니다. 
  • Producer 스레드에서 알림을 받으면 CONSUMER 스레드는 대기열에서 항목을 제거하고 인쇄합니다.

  • 여기에 표시된 Thread.h 헤더가 조건 변수와 뮤텍스를 사용하는 방법을 이해해 보겠습니다.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <string>

using namespace std;

enum ThreadType {
  PRODUCER,
  CONSUMER
};

class Thread {
private:
  static mutex locker;
  static condition_variable untilReady;
  static bool ready;
  static queue<int> appQueue;
  thread *pThread;
  ThreadType threadType;
  bool stopped;
  string name;

  void run();
public:
  Thread(ThreadType typeOfThread);
  ~Thread();
  void start();
  void stop();
  void join();
  void detach();
};
#include "Thread.h"

mutex Thread::locker;
condition_variable Thread::untilReady;
bool Thread::ready = false;
queue<int> Thread::appQueue;

Thread::Thread( ThreadType typeOfThread ) {
  pThread = NULL;
  stopped = false;
  threadType = typeOfThread;
  (CONSUMER == typeOfThread) ? name = "CONSUMER" : name = "PRODUCER";
}

Thread::~Thread( ) {
  delete pThread;
  pThread = NULL;
}

void Thread::run() {
  int count = 0;
  int data = 0;
  while ( 1 ) {
    switch ( threadType ) {
    case CONSUMER: 
    {

      cout << name << " waiting to acquire mutex ..." << endl;

      unique_lock<mutex> uniqueLocker( locker );

      cout << name << " acquired mutex ..." << endl;
      cout << name << " waiting for conditional variable signal..." << endl;

      untilReady.wait ( uniqueLocker, [] { return ready; } );

      cout << name << " received conditional variable signal ..." << endl;

      data = appQueue.front( ) ;

      cout << name << " received data " << data << endl;

      appQueue.pop( );
      ready = false;
    }
      cout << name << " released mutex ..." << endl;
    break;

    case PRODUCER:
    {
      cout << name << " waiting to acquire mutex ..." << endl;
      unique_lock<mutex> uniqueLocker( locker );
      cout << name << " acquired mutex ..." << endl;
      if ( 32000 == count ) count = 0;
      appQueue.push ( ++ count );
      ready = true;
      uniqueLocker.unlock();
      cout << name << " released mutex ..." << endl;
      untilReady.notify_one();
      cout << name << " notified conditional signal ..." << endl;
    }
    break;
  }
  }
}

void Thread::start( ) {
  pThread = new thread ( &Thread::run, this );
}

void Thread::stop( ) {
  stopped = true;
}

void Thread::join( ) {
  pThread->join( );
}

void Thread::detach( ) {
  pThread->detach( );
}
#include "Thread.h"

int main ( ) {

  Thread producer( ThreadType::PRODUCER );
  Thread consumer( ThreadType::CONSUMER );

  producer.start();
  consumer.start();

  producer.join();
  consumer.join();

  return 0;
}

 

 

++ Reference )

https://dydtjr1128.github.io/cpp/2020/04/05/Cpp-lock.html

 

C++ 락(std::lock, std::unique_lock, std::lock_guard, condition_variable...) - dydtjr1128's Blog

Intro std에 존재하는 lock 방법들은 운영체제 단에서 지원하는 크리티컬 섹션을 래핑한 mutex를 기반으로 개발자가 쉽게 락을 걸 수 있도록 도와줍니다. 앞서 배운...

dydtjr1128.github.io

https://jjeongil.tistory.com/1003

 

C++ : condition_variable::wait() : 사용법, 주의사항, 예제, 활용 방법

std::condition_variable::wait 현재 쓰레드 뮤텍스의 잠금을 풀고 notifu_one() 또는 notify_all()을 기다립니다. 깨어나면, 뮤텍스를 다시 잠급니다. 다시말해, notify_xxx가 wait()보다 먼저 호출되면, 해당 쓰레드

jjeongil.tistory.com

 

728x90