이번 시간에는 STL의 동시성 처리 관련 라이브러리를 살펴보겠습니다.

Thread

1. 기본

  • 헤더 :

  • std::this_thread

    • get_id() : 스레드 ID 반환
    • sleep_for : duration 동안 스레드 pause
    • sleep_until : time_point 동안 스레드 pause
    • yield() : 다른 스레드 스케쥴링
int main()
{
    thread::id id = this_thread::get_id();

    cout << id << endl;
    
    this_thread::sleep_for( 3s );
    this_thread::sleep_until( chrono::system_clock::now() + 3s);
    this_thread::yield();

}

2. 생성 방법

  • 기본
#include <iostream>
#include <thread>
using namespace std;

void foo()
{
    cout << "thread start" << endl;
    this_thread::sleep_for(2s);
    cout << "thread end" << endl;
}

int main()
{
    thread t(&foo);

    t.join(); // 스레드 종료 대기.
    //t.detach(); // join과 같지만 주 스레드가 종료되면 같이 종료됨
}
  • 멤버 함수도 수행 가능
#include <iostream>
#include <thread>
using namespace std;

void f1()      {}
void f2(int a) {}

struct Worker
{
    void Main() {}
};

struct Functor
{
    void operator()() {}
};

int main()
{
    thread t1(&f1);
    thread t2(&f2, 5);

    Worker w;
    thread t3(&Worker::Main, &w);

    Functor f; f();
    thread t4(f);

    thread t5( []() {cout << "thread t5" << endl;});


    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();
}

3. Synchronization

  • mutex
#include <iostream>
#include <thread>
#include <string>
#include <mutex>
using namespace std;

int global = 0;
mutex m;

void f1()
{
    lock_guard<mutex> lg(m); // 생성자에서 m.lock, 소멸자에서 m.unlock

    //m.lock();
    global = 100;
    global = global + 1;
    //m.unlock();
}

int main()
{
    thread t1(&f1);
    thread t2(&f1);

    t1.join();
    t2.join();
}
  • promise, future
    • promise : 값을 받을 수 있다는 약속
    • future : 미래의 결과값이 담길 예정인 객체
#include <iostream>
#include <thread>
#include <future>
using namespace std;

void f1( promise<int>& p )
{
    this_thread::sleep_for(3s);
    p.set_value(10);
}

int main()
{
    promise<int> p;
    future<int> ft = p.get_future();

    thread t(&f1, ref(p));
    cout << "wait value " << endl;
    cout << "value : " << ft.get() << endl; // 대기

    t.join();
}

async

1. 스레드를 생성하는 2가지 방법

  • thread 클래스 사용 - low level api
  • async 함수 사용 - high level api
    • async 객체의 소멸자에서 내부적으로 get()이 발생
#include <iostream>
#include <thread>
#include <future>
using namespace std;

int f1()
{
    this_thread::sleep_for(3s);
    return 10;
}

int main()
{
    future<int> ft = async( launch::async, &f1);

    cout << "main routine" << endl;
}

2. std::launch

  • launch policty

    • launch::async : 비동기로 실행(스레드 생성) , 생성 실패시 예외 발생
    • launch::deferred: 지연된 실행(get() 호출시)
    • launch::async | launch::deferred : 생성이 가능하면 생성, 그렇지 않으면 지연된 실행
    int f1()
    {
        cout << "f1 : " << this_thread::get_id() << endl;
        this_thread::sleep_for(3s);
        return 10;
    }
    
    int main()
    {
        cout << "main : " << this_thread::get_id() << endl;
    
        //future<int> ft = async( launch::async, &f1);
    
        // f1을 지연된 실행(get을 호출할때).
        //future<int> ft = async( launch::deferred, &f1);
    
        future<int> ft = async( launch::deferred | launch::async, &f1);
    
        this_thread::sleep_for(1s);
    
        cout << "main routine" << endl;
    
        cout << ft.get() << endl;
    }
    

3. asnyc의 return 값을 수령하지 않았을 때

  • async의 리턴값 임시객체의 소멸자에서 get이 호출되므로 해당 라인에서 block됨
#include <iostream>
#include <thread>
#include <future>
using namespace std;

int f1()
{
    this_thread::sleep_for(3s);
    cout << "f1 end" << endl;
    return 10;
}

int main()
{
    async( launch::async, &f1); // 리턴값으로 future<int> 객체
                                // 이 라인에서 block 되버림

    future<int> ft = async( launch::async, &f1);

    cout << "main routine" << endl;
}