본 시리즈는 C++ 기반의 동시성 프로그래밍에 관한 내용입니다. 이번 시간에는 std::this_tread, chrono에 관해 살펴보겠습니다.

std::this_tread

get_id()

  • 현재 실행중인 스레드의 ID 반환
    • 정수로 변환 불가(구조체 형식으로 리턴됨)
#include <iostream>
#include <thread>

int main()
{
    std::cout << std::this_thread::get_id() << std::endl;

    std::thread::id tid1 = std::this_thread::get_id();
    std::thread::id tid2 = std::this_thread::get_id();
  
    tid1 == tid2;
    tid1 < tid2;

    std::hash<std::thread::id> h;

    std::cout << h(tid1) << std::endl;
}
  • sleep_for() / sleep_until()
#include <iostream>
#include <thread>
#include <chrono>
using namespace std::literals;

std::chrono::system_clock::time_point 
createDateTime(int year, int month, int day, int hour, int minute, int second);

int main()
{
    std::this_thread::sleep_for(std::chrono::seconds(3));
    std::this_thread::sleep_for(3s); // 3ms, 3ns, 3min
    //std::this_thread::sleep_for(3);  // error

    std::chrono::time_point tp1 = std::chrono::steady_clock::now();
    std::this_thread::sleep_until( tp1 + 2000ms );    

    auto tp2 = createDateTime(2021, 4, 11, 12, 39, 00);
    std::this_thread::sleep_until( tp2 );    
}

time_t toUTC(std::tm& timeinfo)
{
#ifdef _WIN32
    std::time_t tt = _mkgmtime(&timeinfo);
#else
    time_t tt = timegm(&timeinfo);
#endif
    return tt;
}

std::chrono::system_clock::time_point 
createDateTime(int year, int month, int day, int hour, int minute, int second)
{
    tm timeinfo1 = tm();
    timeinfo1.tm_year = year - 1900;
    timeinfo1.tm_mon = month - 1;
    timeinfo1.tm_mday = day;
    timeinfo1.tm_hour = hour;
    timeinfo1.tm_min = minute;
    timeinfo1.tm_sec = second;
    tm timeinfo = timeinfo1;
    time_t tt = toUTC(timeinfo);
    return std::chrono::system_clock::from_time_t(tt);
}
  • yield()
    • 현재 스레드의 흐름을 다른 스레드에 양보
#include <iostream>
#include <chrono>
#include <thread>
using namespace std::literals;

void mysleep(std::chrono::microseconds us)
{
    auto target = std::chrono::high_resolution_clock::now() + us;

    while (std::chrono::high_resolution_clock::now() < target)
        std::this_thread::yield();   
} 

int main()
{
    mysleep(1s);
}

std::tread

스레드 생성 방법

  • std::thread의 생성 인자로 스레드를 수행할 함수 전달
  • join() 또는 detach()를 해야함
    • join() : 스레드 종료를 대기
    • detach() : 생성된 스레드가 독립적으로 실행(메인 스레드 종료시 같이 종료됨)
#include <iostream>
#include <thread>
#include <chrono>
using namespace std::literals;

void foo()
{
    for( int i = 0; i < 10; i++)
    {
        std::cout << "foo : " << i << std::endl;
        std::this_thread::sleep_for(100ms);
    }
}
int main()
{
    std::thread t(&foo);

 //   t.join();
    t.detach();
    std::cout << "end" << std::endl;

    int n;
    std::cin >> n;
}
  • 인자 전달 방법
#include <iostream>
#include <string>
#include <thread>

void f1()                { }
void f2(int a, double d) { }
void f3(int a, int& b, std::string&& s) { b = 100;}

int main()
{
    int n = 0;
    std::string s = "hello";

    std::thread t1(&f1);
    std::thread t2(&f2, 10, 3.4);
    std::thread t3(&f3, 10, std::ref(n), std::move(s) );
    t1.join();
    t2.join();
    t3.join();

    std::cout << s << std::endl; // ""
    std::cout << n << std::endl; // 100
}
  • callable object 전달 방법
#include <iostream>
#include <thread>

void foo(int a, double d) {}

struct Machine
{
    void Run(int a, double d) {}
};
struct Work
{
    void operator()(int a, double b) const {}
};
int main()
{
    Machine m;
    Work w;  w(1, 3.4); // 함수객체
    std::thread t1( &foo, 1, 3.4 );
    std::thread t2( &Machine::Run, &m, 1, 3.4 );    
    std::thread t3( w, 1, 3.4 );
    std::thread t4( []{ std::cout << "lambda" << std::endl;} );
    t1.join();
    t2.join();
    t3.join();
    t4.join();
}

std::thread의 멤버 함수

hardware_concurrency() : CPU의 멀티 스레드 지원 개수

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

void foo() 
{
    std::cout << std::this_thread::get_id() << std::endl;
}
int main()
{
    int n =  std::thread::hardware_concurrency();
    std::cout << n << std::endl; // 8

    std::thread t( &foo );
    std::this_thread::sleep_for(1s);

    std::thread::id tid = t.get_id();
    std::cout << "created thread id : " << tid << std::endl;
    t.join();
}

GetCurrentThread : OS 레벨의 스레드 핸들 획득

  • 스레드 우선순위 변경 등에 사용
#include <iostream>
#include <thread>
#include <windows.h>
#include <chrono>
using namespace std::literals;

void foo() 
{
    auto tid = std::this_thread::get_id(); // 스레드 ID 얻기

    auto handle = GetCurrentThread();
    std::this_thread::sleep_for(1s);    
    std::cout << GetThreadPriority( handle) << std::endl;
}

int main()
{
    std::thread t( &foo );
    std::thread::native_handle_type h = t.native_handle();

    std::cout << "ID     : " << t.get_id() << std::endl;
    std::cout << "handle : " << h          << std::endl;

    std::this_thread::sleep_for(100ms);   
    SetThreadPriority((HANDLE)h, THREAD_PRIORITY_TIME_CRITICAL);
    t.join();
}

joinable()

#include <iostream>
#include <thread>

int main()
{
    std::thread t;

    if ( t.joinable() )
    {
        t.join();
    }
}

변수간 thread 객체 전달 방법

  • copy 불가
#include <thread>

void foo() {}
void goo() {}

int main()
{
    std::thread t1(&foo);
    std::thread t2(&goo);

    t1.swap(t2);

//    std::thread t3 = t1;  // error  
    std::thread t4 = std::move(t1);  // ok

//    t1.join(); // 예외.
    t2.join();
    t4.join();
}