SW 그리고 아빠 엔지니어링 중...

아는 만큼 보이고, 경험해봐야 알 수 있고, 자꾸 써야 내 것이 된다.

L C++/Concurrency

[Concurrency] 인자와 callable object

보리남편 김 주부 2023. 7. 24. 18:52

기존 함수를 thread로 실행시킬 수 있을까?🤔

목적 : 설계 때부터 thread 사용을 고려했다면 문제가 없었겠지만, 이미 구현까지 완료된 함수를 thread로 동작시켜야 할 때는 thread 함수를 사용하기 위한 기존 소스의 수정이 필요하다. 최대한 수정 없이 thread로 동작시킬 수 있는 방법은 없는지 알아보자.

Thread 함수 인자


std::thread의 생성자는 "가변인자 템플릿"으로 되어 있어 인자수를 원하는 만큼 늘릴 수 있으며,

thread로 실행시키기 위해서는 호출될 함수를 참조로 전달해야 하고 그 함수에 호출되는 인자를 다음인자로 넘기면 된다. 아래 f1, f2, f3를 thread로 실행하는 아래 예제를 참고해 보자.

#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);  //인자 2개 전달
    
    //참조로 전달
    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
}
참고 : std::thread t1(&f1); 의 의미는 thread가 생성되는 동시에 f1를 thread로 실행시킨다.

f3 함수와 같이 참조로 인자를 전달하는 것도 가능하다. 

std::move(x) 란?
move를 사용하면 이름대로 이동하진 않고 lvalue를 rvalue로 type casting만 한다. 예를 들어 move를 사용하지 않고 s를 인자로 전달 시, s 값이 복사가 되지만 move를 하면 메모리 할당은 발생하지 않고 메모리할당된 소유권이 t3으로 전달된다. (메모리 복사가 없음) 그래서 메모리 복사보다 빠르기에 성능을 향상시킬 수 있지만 move로 소유권이 넘어갔기에 주의하여 사용해야 한다. move 이후 s에 접근 시 에러가 발생한다. (하지만 로그  출력 시에는 null로 출력되는 것으로 확인할 수 있다.) 더 이상 s를 참조하지 않을 때 유용하다.

 

일반 함수뿐만 아니라 다양한 함수도 스레드로 수행할 수 있다.


일반함수뿐만 아니라 / 멤버함수/ 함수 객체 / 람다표현식 등 다양한 함수를 thread로 생성할 수 있다.

단 멤버 함수의 경우 "객체를 인자로 전달" 해야 한다. 아래 예제를 통해서 사용방법을 확인해 보자.

#include <iostream>
#include <thread>

struct Machine
{
    void Run(int a, double d) {
        std::cout << __func__ << " a : " << a << " d : " << d << "\n"<< std::endl;
    }
};
struct Work
{
    void operator()(int a, double b) const {
        std::cout << __func__ << " a : " << a << " b : " << b << "\n"<< std::endl;
    }
};

int main()
{
    // 모든 종류의 callable object 를 스레드로 수행 가능

    // 1. 멤버 함수
    Machine m;
    std::thread t1(&Machine::Run, &m, 1, 3.4);
                                // m.Run(1, 3.4) 를 스레드로

    // 2. 함수객체
    Work w; // 객체지만
    w(1, 3.4); // 함수 처럼 사용가능
    std::thread t2(w, 1, 3.4); // 주의 &w 가 아닌 w. 

    // 3. 람다 표현식을 스레드로 => 요즘 유행.
    std::thread t3([](int a) {std::cout << "lambda\n"; }, 3);

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

 

참고


* 본 글에 예제 코드는 코드누리 교육을 받으면서 제공된 샘플코드를 활용하였습니다.

728x90