std::thread 사용법
간단히 스레드를 만들어 테스트해야 할 일이 있어 std::thread를 찾아보고 만들어봤다.
예전에는 boost::thread를 이용했었는데 C++11에 오면서 아예 다 표준에 들어왔다는 게 놀랍고 신기하다.
2026년 수정 안내 원래 실었던 예제에 문제가 세 가지 있었다. (1)
char* target_msg = "test";는 C++11부터 컴파일되지 않는다. (2) 두 스레드가 동기화 없이 같은 전역 버퍼에strcpy_s를 하는 데이터 레이스 코드였다. (3)while(true)때문에join()이 영원히 반환되지 않아 프로그램이 끝나지 않았다. 스레드 글에 실을 예제로 적절하지 않아 다시 작성한다.
기본 사용법
#include <iostream>
#include <thread>
#include <chrono>
void CallbackFunction( int thread_num )
{
for ( int i = 0; i < 5; ++i )
{
std::cout << "working (" << thread_num << ")\n";
std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
}
}
int main()
{
std::thread thread1( CallbackFunction, 1 );
std::thread thread2( CallbackFunction, 2 );
thread1.join();
thread2.join();
return 0;
}
std::this_thread::sleep_for를 쓰면 windows.h의 Sleep이 필요 없다. 표준이라 리눅스에서도 그대로 빌드된다.
join()은 해당 스레드가 끝날 때까지 기다린다. 따라서 스레드 함수는 언젠가 반환되어야 한다. 무한 루프를 돌아야 한다면 종료 플래그를 두고 빠져나올 수 있게 만들어야 한다.
공유 데이터에는 반드시 동기화가 필요하다
여러 스레드가 같은 버퍼에 동시에 쓰면 데이터 레이스가 되고, 이는 정의되지 않은 동작이다. 운 좋게 동작하는 것처럼 보이다가 부하가 걸리면 깨진다.
#include <mutex>
#include <string>
std::mutex g_msg_mutex;
std::string g_msg;
void CallbackFunction( int thread_num )
{
for ( int i = 0; i < 1000; ++i )
{
std::lock_guard<std::mutex> lock( g_msg_mutex );
g_msg = "test " + std::to_string( thread_num );
}
}
std::string을 쓰면 버퍼 크기를 직접 관리할 필요도 없어진다.
종료 플래그를 두는 패턴
#include <atomic>
std::atomic<bool> g_running{ true };
void WorkerFunction()
{
while ( g_running.load( std::memory_order_relaxed ) )
{
// ... 작업 ...
std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
}
}
int main()
{
std::thread worker( WorkerFunction );
// ... 잠시 후 ...
g_running = false;
worker.join();
return 0;
}
플래그를 bool이 아니라 std::atomic<bool>로 둔 것이 중요하다. 일반 bool에 여러 스레드가 접근하면 그 자체가 데이터 레이스이고, 컴파일러가 루프 밖으로 읽기를 끌어내 무한 루프가 되기도 한다.
join을 잊으면 프로그램이 죽는다
std::thread 객체가 소멸될 때 아직 join도 detach도 되지 않았다면 std::terminate()가 호출된다. 예외가 던져지는 경로까지 고려하면 실수하기 쉬운 부분이다.
C++20부터는 std::jthread 가 있다. 소멸자에서 알아서 join해주고, 협조적 종료를 위한 stop_token까지 제공한다.
#include <thread>
void WorkerFunction( std::stop_token token )
{
while ( !token.stop_requested() )
{
// ... 작업 ...
}
}
int main()
{
std::jthread worker( WorkerFunction );
return 0; // 소멸자가 stop 요청 후 join까지 해준다
}
댓글 남기기