-
쓰레드게임서버/멀티쓰레드 프로그래밍 2022. 3. 9. 13:39
1. 쓰레드 생성
using System; using System.Threading; // 쓰레드를 사용하기 위해 선언 namespace ServerCore { class Program { static void MainThread() // 쓰레드가 실행할 함수 { Console.WriteLine("Hello Thread!"); } static void Main(string[] args) { Thread t = new Thread(MainThread); // 쓰레드 생성, 생성자에는 쓰레드가 담당할 함수 넣기 t.Start(); // 쓰레드 시작 함수 Console.WriteLine("Hello World!"); } } }
2. 백그라운드 쓰레드
쓰레드가 종료되지 않으면 메인함수도 종료되지 않는 문제가 생긴다.
쓰레드는 기본적으로 Foreground Thread로 설정되어 있는데 이것을 Background Thread로 설정하면 메인함수가 종료될 때 쓰레드도 자동으로 종료할 수 있다.
using System; using System.Threading; namespace ServerCore { class Program { static void MainThread() { while (true) // 무한 루프 { Console.WriteLine("Hello Thread!"); } } static void Main(string[] args) { Thread t = new Thread(MainThread); t.IsBackground = true; // 백그라운드 쓰레드로 설정 t.Start(); Console.WriteLine("Hello World!"); } } }
isBackground가 기본적으로 false로 설정되어 있는데 true로 설정하면 된다.
3. Join
백그라운드를 true로 설정하면 메인함수는 쓰레드가 실행되건 말건 종료된다는 것을 2번에서 알아보았다.
그런데 쓰레드의 일을 다 끝내고 메인함수를 실행시키고픈 경우도 생길 수 있는데 이럴 때는 Join함수를 쓰면된다.
using System; using System.Threading; namespace ServerCore { class Program { static void MainThread() { while (true) { Console.WriteLine("Hello Thread!"); } } static void Main(string[] args) { Thread t = new Thread(MainThread); t.Name = "Test Thread"; // 쓰레드에게 이름을 넣어줌 t.IsBackground = true; Console.WriteLine("Waiting for Thread"); t.Start(); t.Join(); // 쓰레드가 종료될 때까지 기다려줌 Console.WriteLine("Hello World!"); } } }
4. ThreadPool
쓰레드를 생성하는 것은 부담이 큰 작업이다. 따라서 이러한 문제점을 해결하기 위해 ThreadPool을 사용한다.
ThreadPool은 미리 쓰레드를 생성하고 필요할 때마다 생성한 쓰레드를 꺼내쓰는 방법인데 이는 유니티에서 오브젝트 풀링과 매우 유사하다.
using System; using System.Threading; namespace ServerCore { class Program { static void MainThread(object state) // ThreadPool을 사용하기 위해선 인자로 object형을 추가해야한다. { for (int i = 0; i < 5; i++) { Console.WriteLine("Hello Thread!"); } } static void Main(string[] args) { ThreadPool.QueueUserWorkItem(MainThread); // ThreadPool이 실행시킬 함수 대입 while (true) { // ThreadPool은 백그라운드 쓰레드이기 때문에 // Main함수를 유지하기 위해서 일시적으로 무한루프를 넣음. } } } }
이 방식은 작업을 처리할 때 기존에 있는 쓰레드가 와서 처리하는 방법이다.
using System; using System.Threading; namespace ServerCore { class Program { static void MainThread(object state) // ThreadPool을 사용하기 위해선 인자로 object형을 추가해야한다. { for (int i = 0; i < 5; i++) { Console.WriteLine("Hello Thread!"); } } static void Main(string[] args) { ThreadPool.SetMinThreads(1, 1); // 최소쓰레드풀 1개 ThreadPool.SetMaxThreads(5, 5); // 최대쓰레드풀 5개 for(int i = 0; i < 5; i++) // 5개의 쓰레드를 모두 사용 { ThreadPool.QueueUserWorkItem( (obj) => { while (true) { } } ); // 무한 루프 } ThreadPool.QueueUserWorkItem(MainThread); // 사용할 수 있는 쓰레드가 없으니까 실행이 안됨! // 만약 for문의 조건을 i<4로 하면 1개의 쓰레드가 남기 때문에 실행이 됨. while (true) { } } } }
위와같이 만약 작업을 처리할 쓰레드가 모두 다른 작업을 하고 있는 상태라면 새로운 쓰레드를 생성하는 것이 아닌 계속 대기하는 상태에 놓여지게 되는 문제점이 있다.
5. Task
ThreadPool에는 작업할 쓰레드가 없다면 다른 쓰레드의 작업이 끝날 때까지 기다려야 하는 문제점이 있었다.
Task는 이러한 문제점을 해결할 수 있다.
using System; using System.Threading; using System.Threading.Tasks; // Task를 사용하기 위해 선언 namespace ServerCore { class Program { static void MainThread(object state) { for (int i = 0; i < 5; i++) { Console.WriteLine("Hello Thread!"); } } static void Main(string[] args) { ThreadPool.SetMinThreads(1, 1); // 최소쓰레드풀 1개 ThreadPool.SetMaxThreads(5, 5); // 최대쓰레드풀 5개 for (int i = 0; i < 5; i++) // 5개의 쓰레드를 모두 사용 { // 두번재 인자에 TaskCreationOptions.LongRunning을 추가해주면 무한정 대기를 방지할 수 있음 Task t = new Task(() => { while (true) { } }, TaskCreationOptions.LongRunning); t.Start(); // Task 실행 } ThreadPool.QueueUserWorkItem(MainThread); // 사용할 쓰레드가 없음에도 실행이 됨! while (true) { } } } }
'게임서버 > 멀티쓰레드 프로그래밍' 카테고리의 다른 글
Interlocked (0) 2022.03.10 메모리 베리어 (0) 2022.03.10 캐시 이론 (0) 2022.03.09 컴파일러 최적화 (0) 2022.03.09 멀티쓰레드 개론 (0) 2022.03.09