서버/멀티스레드

1. 스레드 생성

광란의슈가슈가룬 2024. 7. 10. 03:39
cout << "Hello World!" << endl;

위와 같은 작업은 단순한 출력이 아니라 생각보다 무거운 작업이다.

유저 레벨에서 간단히 할 수 있는 덧셈, 뺄셈이 아니라 모니터에 직접 출력하는 작업은 OS 커널에 요청을 하여 운영체제가 작업을 수행하는 것이다. (System Call)

 

스레드 생성 또한 System Call로 운영체제에 요청을 보내야 한다. 이는 운영체제마다 API 함수가 다르므로 작업 환경을 고려해야 한다.

#include <Window.h>

std::CreateThread()

이렇게 윈도우 API를 활용하게 될 경우 이후 리눅스로 넘어갈 경우 수정사항이 굉장이 많아진다.

물론 서버의 경우 그럴 일은 거의 없다.

#include <thread>

그래도 위와 같이 모든 환경에서 작동할 수 있는 공용 라이브러리를 사용하는 것이 좋다.

 

-스레드 생성

#include <thread>

void HelloThread()
{
	cout << "Hello Thread!" << endl;
}

int main()
{
	thread t(HelloThread);
}

독립적으로 실행되는 스레드를 생성한다.

int main()
{
	HelloThread();
    
	thread t(HelloThread);
}

두 코드의 차이점은 HelloThread()의 경우 main이 실행중일 때 HelloThread()를 호출한 후 다시 돌아오지만

스레드로 생성하게 되면 main 스레드는 그대로 실행이 되고 t라는 스레드가 새로 만들어져 병렬로 실행된다.

main과 HelloThread가 독립적으로 동시에 실행되는 것이다.

 

#include <thread>

void HelloThread()
{
	cout << "Hello Thread!" << endl;
}

int main()
{
	thread t(HelloThread);

	cout << "Hello Main!" << endl;
}

그렇다면 위와 같이 코드를 작성하면 어떻게 될까?

문제가 없는 코드처럼 보이지만 에러가 뜬다.

main 스레드에서 t라는 변수로HelloThread를 관리하는데 이때 main이 먼저 끝나버리면 에러가 발생하게 된다.

따라서 main스레드는 다른 스레드가 종료될 때까지 기다려야 한다.

 

#include <thread>

void HelloThread()
{
	cout << "Hello Thread!" << endl;
}

int main()
{
	thread t(HelloThread);

	cout << "Hello Main!" << endl;
    
	t.join();
}

이렇게 코드를 수정하게 되면 t스레드가 종료될 때까지 main스레드가 기다리게 된다.

스레드의 종료 시점은 일반적인 함수와 마찬가지로 매개변수로 던져준 HelloThread가 종료될 때 스레드도 종료된다.

t.hardware_concurrency(); //CPU 코어 개수, 정보 추출 불가 -> 0
t.get_id();	 	  //스레드마다 id, 스레드의 구분이 필요할 때
t.detach();		  //객체에서 실제 스레드를 분리, t로 스레드의 정보 추출 불가
t.joinable();	 	  //객체에 스레드가 있는지 확인
t.join();	 	  //스레드가 종료될 때까지 기다림

if(t.joinable())
	t.join(); //일반적인 방식

몇 가지 스레드 함수들이다. 이런게 있다 정도로만 알아두자.

주로 사용되는 함수는 joinable, join이다.

void HelloThread_2(int num)
{
	cout << num << endl;
}

int main()
{
	thread t(HelloThread_2, 32);

	if (t.joinable())
		t.join();
}

스레드에 인자를 넘겨주고 싶을 때는 스레드를 생성할 때 인자를 함께 넘겨주면 된다.

void HelloThread_2(int num)
{
	cout << num << endl;
}

int main()
{
	vector<thread> v;

	for (int i = 0; i < 10; i++)
	{
		v.push_back(thread(HelloThread_2, i));
	}

	for (int i = 0; i < 10; i++)
	{
		if (v[i].joinable())
			v[i].join();
	}
    
	cout << "Hello Main!" << endl;	
}

스레드를 10개 생성하는 간단한 코드이다.

 

 

출력 결과

일반적으로 생각하는 결과와 다른 모습인데 이는 스레드들이 병렬적으로 실행되기 때문에 숫자들이 이어져서 출력되거나 endl이 나중에 실행되는 일이 생길 수 있다. 위 코드는 실행할 때마다 결과가 다르다.

 

이후 스레드들은 위와 같은 단순한 작업이 아니라 DB관리, 네트워크 패킷처리 등 각각의 작업을 수행할 것이다.

'서버 > 멀티스레드' 카테고리의 다른 글

6. Sleep  (0) 2024.07.17
5. SpinLock  (1) 2024.07.14
4. DeadLock  (0) 2024.07.11
3. Lock  (0) 2024.07.11
2. Atomic  (0) 2024.07.10