C++11에 동시성 지원 라이브러리가 추가되었고 익히 알고 있는 thread와 관련된 라이브러리이다.
std 네임스페이스에 속하며(표준 패키지임) thread 헤더를 추가해야 사용할 수 있다.
main thread가 새로 thread를 생성하게 되면, 이 worker thread를 기다릴지, 기다리지 않을지 두 가지 전략이 존재한다.
우선은 worker thread가 작업을 종료할 때까지 main thread가 기다리는 전략의 예제를 살펴보자. worker thread는 foreground에서 실행되며 main thread는 worker thread 종료까지 block된다.
#include <iostream>
#include <thread>
using namespace std;
void workerFunction() {
cout << "Worker thread is sleeping for 2 seconds" << endl;
this_thread::sleep_for(chrono::seconds(2));
cout << "Worker thread is done." << endl;
}
int main() {
thread workerThread(workerFunction);
workerThread.join();
cout << "Main thread is done." << endl;
return 0;
}
여러 개의 worker thread를 생성하는 간단한 예제이다.
vector에 담아두고 하나씩 join한다.
#include <iostream>
#include <thread>
#include <vector>
using namespace std;
void workerFunction(int id) {
this_thread::sleep_for(chrono::seconds(2));
}
int main() {
const int numThreads = 10;
vector<thread> threads;
for (int i = 0; i < numThreads; ++i) {
threads.emplace_back(thread(workerFunction, i + 1));
}
for (int i = 0; i < numThreads; ++i) {
threads[i].join();
}
cout << "Main thread is done." << endl;
return 0;
}
이 예제 프로그램을 실행하면 거의 2초의 실행 시간을 가지고 작업이 종료된다. 동시성이 10개 thread에 의해 높게 형성되므로 순차 실행과 달리 20초가 아니라 2초 정도면 완료하는 것이다.
main thread는 worker thread를 기다리지 않고 worker thread는 background에서 실행된다. main thread는 block되지 않고 자신의 작업을 계속 수행한다.
#include <iostream>
#include <thread>
using namespace std;
void workerFunction() {
cout << "Worker thread is sleeping for 2 seconds" << endl;
this_thread::sleep_for(chrono::seconds(2));
cout << "Worker thread is done." << endl;
}
int main() {
thread workerThread(workerFunction);
workerThread.detach();
cout << "Main thread is done." << endl;
this_thread::sleep_for(chrono::seconds(3));
return 0;
}
Main thread is done.
Worker thread is sleeping for 2 seconds
Worker thread is done.
만약 main thread가 sleep_for()를 이용해서 기다리지 않는다면 worker thread가 시작하기 전에 프로그램이 종료할 수도 있고, worker thread가 실행되더라도 작업 완료 후 자원이 정상적으로 해제되지 않고 강제 종료된다.
Main thread is done.
terminate called without an active exception
[1] 2654421 abort (core dumped) ./a.out
(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007ffff7bbb859 in __GI_abort () at abort.c:79
#2 0x00007ffff7e448d1 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007ffff7e5037c in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007ffff7e503e7 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00005555555554d7 in std::thread::~thread (this=0x7fffffffda58, __in_chrg=)
at /usr/include/c++/9/thread:139
#6 0x00005555555553af in main () at thread_without_join.cc:14
thread의 소멸자 수행 중에 알 수 없는 런타임 예외가 발생하여 ABORT 시그널을 발생시키고 종료한다.
thread의 기본적인 지식을 아는 사람이라면 이렇게 코드를 작성할리 없지만, 혹시 이렇게 작성해서 실행하는 경우가 있다면 올바른 컨텍스트가 아니라서 join() 실행 중에 예외가 발생한다.
terminate called after throwing an instance of 'std::system_error'
what(): Invalid argument
[1] 2653638 abort (core dumped) ./a.out
(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007ffff7bbb859 in __GI_abort () at abort.c:79
#2 0x00007ffff7e448d1 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007ffff7e5037c in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007ffff7e503e7 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00007ffff7e50699 in __cxa_throw () from /lib/x86_64-linux-gnu/libstdc++.so.6
#6 0x00007ffff7e476fd in std::__throw_system_error(int) () from /lib/x86_64-linux-gnu/libstdc++.so.6
#7 0x00007ffff7e7d070 in std::thread::join() () from /lib/x86_64-linux-gnu/libstdc++.so.6
#8 0x00005555555553a8 in main () at thread_without_join.cc:17