새로운 thread에 argument 전달 방법
바로 예제 코드를 보자.
Example Code
#include <iostream>
#include <thread>
#include <string>
void fn(int a, std::string s)
{
std::cout << a << s << std::endl;
}
int main()
{
int num = 42;
std::string s = "code";
std::thread t1(fn, num, s);
t1.join();
return 0;
}
위와 같이 fn 뒤에 argument를 순차적으로 넣어주면 된다. 그런데 fn은 argument로 value를 전달받고 있기 때문에 Copy가 일어나고 있다. Copy 동작은 argument의 크기에 따라서 비싼 동작이 될 수 있다. 그러한 경우에는 reference로 넘겨주면 된다. reference로 넘겨주기 위해서 std::ref 로 감싸준다.
Example Code
#include <iostream>
#include <thread>
#include <string>
void fn(int a, std::string& s)
{
std::cout << a << s << std::endl;
}
int main()
{
int num = 42;
std::string s = "code";
std::thread t1(fn, num, std::ref(s));
t1.join();
return 0;
}
하지만 여기서 조심해야 할 것이 있는데 ref로 전달받은 변수나 오브젝트의 Life Cycle이다. 이러한 문제는 Single Thread Process에선 일어나지 않지만 Multi Thread Process에서는 일어나기 쉽다.
Example Code
#include <iostream>
#include <thread>
#include <string>
#include <chrono>
void fn(int& a)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << a << std::endl;
}
void threadCaller(std::thread& t)
{
int num = 42;
t = std::thread(fn, std::ref(num));
}
int main()
{
std::thread t1;
threadCaller(t1);
t1.join();
return 0;
}
예시에서는 42가 출력되어야 할 것 같지만 쓰레기 값이 나온다.
메모리 접근
실행 과정
- main 함수에서 Thread(t1)을 생성
- threadCaller 함수가 불리면서 숫자 42를 만들고 함수 fn을 실행시키기 위해 새로운 Thread 생성
- 만들어진 Thread는 main 함수에서 만든 Thread(t1)에서 관리한다.
- 함수 fn은 넘겨받은 숫자 42를 출력하면 되는데 이 때 1초의 딜레이를 넣게 되면서 그 사이 main thread는 thread Caller의 실행을 마치고 해당 함수를 pop한다.
- 이어서 Thread t1이 끝나길 기다린다.
함수 fn은 넘어온 a 값을 출력해야하는데 넘어온 num 값은 이미 pop이 되었기 때문에 쓰레기 값이 나오게 된다.
이러한 문제 때문에 thread는 같은 scope level에서 join 해주는 것이 안전하다.
void threadCaller(std::thread& t)
{
int num = 42;
t = std::thread(fn, std::ref(num));
t.join();
}
Return Value는 어떻게 받을 수 있을까?
결론부터 말하자면 return 값을 직접적으로 받을 방법은 없다.
이를 받을 수 있는 방법으로는
- reference argument로 받는다.
- 람다 함수를 사용한다.
(안전하게 받기 위한 방법으로 async, future-promise 등이 있지만 여기서는 다루지 않음.)
Example Code : reference 전달
#include <iostream>
#include <thread>
#include <string>
#include <chrono>
void fn(int& a)
{
a = 2;
}
int main()
{
int num = 42;
std::thread t1(fn, std::ref(num));
t1.join();
return 0;
}
Example Code : 람다(lambda) 함수 사용
return 값을 전달받을 수 있는 트릭으로 다음과 같이 람다 함수를 사용할 수 있다.
#include <iostream>
#include <thread>
#include <string>
#include <chrono>
int fn()
{
return 3;
}
int main()
{
int num = 0;
std::thread t1([&num]()
{
num = fn();
});
t1.join();
std::cout << num << std::endl;
return 0;
}
thread 내 exception을 던질 경우 어떻게 받을 것인가?
다른 thread에서 던진 exception을 main thread에서 직접적으로 잡는 방법은 없다. 다른 thread에서 exception이 던져지면 terminate 함수가 호출된다. 이러한 경우에 exception을 받고 싶거나 핸들링 하기 위해서는 std::promise 혹은 shared variable(mutex, atomic)을 통해서 해야한다.
'C++ > Thread' 카테고리의 다른 글
[Thread] Mutex(뮤텍스) (0) | 2022.07.15 |
---|---|
[Thread] std::jthread (조이닝 쓰레드) (0) | 2022.07.13 |
[Thread] thread_local (0) | 2022.06.20 |
[Thread] std::thread (0) | 2022.06.20 |
[Thread] Introduction (0) | 2022.06.13 |