프로그래밍/C언어

[씹어먹는 C++] 16 - 0 쓰레드 생성과 종료

LinZBe4hia 2022. 2. 28. 22:00

회사에서 파노라마 영상 생성하는 도중 속도 최적화를 위해 쓰레드를 적용해야 하는 경우가 생겼습니다. 이 일을 계기로 씹어먹는 c++이라는 책을 알게 되었고,  thread 공부한 것들을 정리해서 적어보겠습니다. 

 

 

C++11 에서부터 표준에 쓰레드가 추가되면서 쓰레드 사용이 매우 편리해졌습니다.

기본 코드는 다음과 같다. 

#include <iostream>
#include <thread>
using std::thread;

void f1() {
	for (int i = 0; i < 10; i++) 
		std::cout << "thread 1 running!\n" ;
}

void f2() {
	for (int i = 0; i < 10; i++) 
		std::cout << "thread 2 running! \n";	
}

void f3() {
	for (int i = 0; i < 10; i++) 
		std::cout << "thread 3 running! \n";	
}

int main() {
	thread t1(f1);
	thread t2(f2);
	thread t3(f3);

	t1.join();  // return the data from function in thread t1
	t2.join();  // return the data from function in thread t2
	t3.join();  // return the data from function in thread t3

	// After join(), all the functions in thread is completed 
	std::cout << "Done! -- 1 \n" ;
}

실행결과는 너무 길기 때문에 생략. 중요한 점은 마지막 "Done! --1\n"은모든 함수가 각각 10번씩 수행되고 마무리된 뒤에 출력된다. 그 이유는 .join() 함수 때문이다. 

join 은, 해당하는 쓰레드들이 실행을 종료하면 리턴하는 함수이다. 따라서 t1.join()의 경우 t1이 종료하기 전까지 리턴하지 않는다. 그 말은 t2.join()이 실행되는 건 t1이 끝난 후인 것이다. t3.join()도 마찬가지. 만약 t1.join()을 thread t2(f2) 앞에 놓아버리면 직렬로 프로그램이 수행되는 거나 마찬가지므로 코드할 때 유의해야 한다. 

 

만약 join을 하지 않으면 어떻게 될까?

int main() {
  thread t1(f1);
  thread t2(f2);
  thread t3(f3);
    
  cout << "Done! --1\n";
}

그럼 Windows에서는 다음과 같이 성공적으로 프로그램이 종료되지 못한다. 

그 이유는 쓰레드들의 내용이 모두 실행되기 전에 main 함수가 종료되면서 쓰레드 객체들 (t1, t2, t3)의 소멸자가 호출되었기 때문이다. C++ 표준에 따르면 join이나 detach되지 않은 쓰레드들의 소멸자가 호출되면 위처럼 예외를 발생시킨다.

여기서 detach는 "해당 쓰레드를 실행시킨 후 잊어버리는 것"이라고 생각하면 된다. detach가 있다면 쓰레드는 알아서 백그라운드에서 돌아가게 된다.  다음 예제를 보자.

int main() {
	thread t1(f1);
	thread t2(f2);
	thread t3(f3);

	t1.detach(); 
	t2.detach();
	t3.detach();

	std::cout << "Done! -- 1 \n" ;
}

결과는 여러가지 형태로 나올 수 있는데 기본적으로 프로세스가 종료될 때 해당 프로세스 안에 있는 모든 쓰레드들은 종료 여부와 상관없이 자동으로 종료된다. 그래서 결국 메인함수가 종료되면 디버그 콘솔창에서 thread running! 을 출력할 수 없게 된다. 

즉 detach()를 쓸 경우 main 함수에서 더이상 쓰레드가 종료될 떄까지 기다리지 않고, 종료하는 순간 thread 내용은 백그라운드로 돌아가게 된다. 

 

 

쓰레드에 인자 전달하기