(C++ : Concurrency) 2. thread - 2

Posted by : at

Category : Cpp


std::thread 스레드 만들기

#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();   // OR t.detach();
}

매개변수 넘기기

#include <iostream>
#include <thread>
#include <string>

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)); // 그냥 n으로 넘기면 안됨을 기억
    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;

    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가 지원하는 함수

#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();    // CPU가 지원하는 thread개수
    std::cout << n << std::endl;

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

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

native_handle_type

std::thread의 thread우선순위를 변경할수 있을까?
C++ 표준에서는 미지원 OS의 도움을 받아야한다.

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

void foo()
{
    // thread의 핸들 얻기
    auto tid = std::this_thread::get_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();  // OS의 스레드 핸들 반환

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

    std::this_thread::sleep_for(100ms);
    // thread의 우선순위 변경
    SetThreadPriority(h, THREAD_PRIORITY_TIME_CRITICAL);
    t.join();
}

thread 복사와 이동

#include <thread>

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

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

    t1.swap(t2);
    // t1 : goo / t2 : foo 실행

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

    //t1.join();
    t2.join();
    t3.join();
}

std::ref

#include <iostream>
#include <functional>

void foo(int& a) { a = 200; }

template<typename T> void call_foo(T arg)
{
    foo(arg);
}

int main()
{
    int n = 0;

    //foo(n);
    call_foo(n);    // n이 복사본이 넘어가게 된다.

    std::cout << n << std::endl;    // 0
}

call_foo(n);를 해도 값 변화가 나타나게 하고 싶다면?

#include <iostream>
#include <functional>

void foo(int& a) { a = 200; }

template<typename T> void call_foo(T arg)
{
    foo(arg);
}

int main()
{
    int n = 0;

    call_foo(std::ref(n)); 

    std::cout << n << std::endl;    // 200
    // 이게 어떻게 가능하지?
}

어떻게 가능한지 구현하며 설명

#include <iostream>

template<typename T> struct reference_wrapper
{
    T* obj
public:
    reference_wrapper(T& t) : obj(&t) {}    // 참조로 받아서 그 주소를 보관한다
    operator T&() { return *obj; }
};

int main()
{
    int n = 0;
    reference_wrapper<int> rw = n;

    int& r = rw;    // operator T&()가 호출됨
    r = 100;

    std::cout << n << std::endl;    // 100
}

결국 객체를 주소로 보관하다가 T&로 암시적변환해서 리턴해준다가 핵심

// 3. foo에서 a의 참조형을 받기에 reference_wrapper에서 관리하던 참조형 리턴
void foo(int& a) { a = 200; }

template<typename T> void call_foo(T arg)
// 2.
// T : reference_wrapper가 되고 arg는 reference_wrapper형태로 하나 더 복사된다.
// 단, 복사되며 내부에 가지고 있던 n의 주소값도 같이 복사가 된다(핵심)
{
    foo(arg);
}

int main()
{
    int n = 0;

    reference_wrapper<int> rw = n;  // 1. rw에서 n의 주소를 담고있다
    call_foo(rw); 

    std::cout << n << std::endl; 
}

About Taehyung Kim

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

Star
Useful Links