Thread

headkio·2020년 9월 20일
0

C++

목록 보기
35/35
post-thumbnail

Thread ?

C++에서 지원하지 않아 OS마다 구현 방법이 달랐다.
OS Lib를 가져다 써야하던 Thread 기능이 생겼다. (C++11)

std::thread

표준 c++ 쓰레드
이동 가능
복사 불가능

생성

생성자 1

thread() noexcept;

template<class Function, class... Args>
explicit thread(Function&& f, Args&&... args);
std::thread emptyThread;
std::thread printThread(PrintMsg, 10); // void PrintMsg(int count);

생성자 2

thread(thread&& other) noexcept;
thread(const thread&) = delete;
std::thread printThread(PrintMsg, 10); 
std::thread movedThread(std::move(printThread)); // printThread는 더이상 쓰레드가 아님
std::thread copiedThread = movedThread; // ERROR ! 복사 생성자 없음

get_id()

Thread의 고유 ID를 가져온다.
ID가 꼭 Int형으로 정의된 것이 아니기 때문에 ID 타입을 사용해야한다.
OS별로 ID를 다르게 관리하기 때문이다.

std::thread::id childThreadID = thread.get_id();

join()

int main() 
{
  std::thread thread(PrintMsg, "msg from a chil thread");
  thread.join();
}

join된 쓰레드가 끝날때까지 기다린다.

detach()

int main() 
{
  std::thread thread(PrintMsg, "msg from a chil thread");
  thread.detach();
  
  if (thread.joinable())  // join 가능한지 확인
  {
    thread.join(); // detach된것은 join 불가
  }
}

메인 쓰레드와 무관하게 독립적으로 실행되도록 떼어낸다

std::this_thread

sleep_for()

현재 thread 실행을 멈춘다.

std::this_thread::sleep_for(std::chrono::seconds(3));

yield()

다른 쓰레드가 갈수 있도록 양보한다.

std::this_thread::yield();

std::mutex

공유 자원을 잠근다.
lock 되어 있으면 다른 thread가 접근 못한다.

void PrintMsg(const std::string& message)
{
  static std::mutex m;
  
  m.lock();
  std::cout << message << std::endl;
  m.unLock();
}

int main()
{
  std::thread thread(PrintMsg, "Message from a child thread.");
  PrintMsg("Msg from a main thread.");
  thread.join();
  
  return 0;
}

recursive_mutex

재귀함수를 사용하여 같은 쓰레드에서 또 lock을 시도하려고 할 경우 deadlock 걸릴 수 있다.
이럴 경우 사용

std::scoped_lock()

c++ 17부터 지원.
일반 lock 보다는 scoped_lock을 사용하자 !
여러 mutex를 동시에 걸 수 있다.
{ }를 지정해서 최소 범위에만 걸도록 하자.

{  
  static std::mutex m;
  
  {
    std::scoped_lock<std::mutex> lock(m);
    std::cout << message << std::endl;
  }
}

std::condition_variable

이벤트 개체
신호를 받을 때까지 현재 쓰레드의 실행을 멈춤.

멈춰 놓은 Thread 하나 혹은 전부를 실행함

  • notify_one()
  • notify_all()

조건을 충족할때까지 실행을 멈춤

  • wait()
  • wait_for()
  • wait_until()
template<class Predicate>
void wait(std::unique_lock<std::mutex& lock, Predicate pred);
while (!pred())
{
  wait(lock);
}

두 번째 변수 pred가 false일때만 lock을 걸게 할 수 있다.
잘못 깨어날 위험을 줄인다.

std::unique_lock

  • 기본적으로 scoped_lock
  • condition_variable에 쓸 수 있는 유일한 lock
  • 생성시에 lock하지 않을 수 있음 (두번쨰 매개변수로 std::defer_lock 전달)

Example

#include <iostream>
#include <mutex>
#include <queue>

static std::mutex sQueueLock;
static std::condition_variable sEvent;
static std::queue<int> sQueue;

void Consume()
{
  while (true)
  {
    int val;
    {
      std::unique_lock<std::mutex> lock(sQueueLock);
      sEvent.wati(lock, [] { return !sQueue.empty(); });
      
      val = sQueue.front();
      sQueue.pop();
    }
    
    std::cout << val << std::endl;
  }
}

void Produce()
{
  std::unique_lock<std::mutex> lock<sQueueLock);
  sQueue.push(1);
  
  sEvent.notify_one();
}

int main()
{
  std::thread producer(Produce);
  std::thread consumer(Consue);
  
  consumer.join();
  producer.join()
  
  return 0;
}
profile
돌아서서 잊지말고, 잘 적어 놓자

0개의 댓글