ThreadPool 클래스와 Task 클래스를 이용하여 쓰레드를 생성해보자.
쓰레드의 수의 제한이 없다보면 CPU와 쓰레드를 연결하는데 지나치게 많은 시간이 소모된다.
ThreadPool 클래스의 경우, 작업을 하는 쓰레드의 개수가 정해져있다. 따라서 작업이 많을 시 대기 시간이 필연적으로 발생한다.
아래 코드를 살펴보자.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ServerCore
{
class Program
{
static void MainThread(object state)
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Hello World");
}
}
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); // 다른 쓰레드에 MainThread 함수를 실행하도록 요청
while (true) { } // Background Thread가 완료되기 전에 application이 종료되지 않게 한다.
}
}
}
위 코드의 결과 아무런 동작이 일어나지 않는다.
for 문에서 이미 5개의 쓰레드가 전부 while문을 반복하기에
MainThread를 실행시킬 여분의 쓰레드가 남아있지 않았다.
아래와 같이 코드를 수정해보자.
for(int i=0;i<4;i++) // 4개의 쓰레드를 사용하도록 변경
{
ThreadPool.QueueUserWorkItem((obj) =>
{ while (true) { } });
}
1개의 남은 쓰레드로 MainThread 함수가 실행되는 것을 확인할 수 있다.
CPU에 부하가 걸리는 것을 막을 수 있겠지만 처리방식이 굉장히 비효율적이다. 쓰레드가 부족하면 스케줄러 자동으로 여분의 쓰레드를 요청할 수 있는 방법이 없을까?
이 문제를 해결해주는 것이 바로 Task 클래스이다.
for (int i = 0; i < 5; i++)
{
Task t = new Task(delegate () { while (true) { } }
, TaskCreationOptions.LongRunning);
// ThreadPool에서 뽑아 쓰는 것이 아닌 스케줄러에 요청한 별도의 Thread로 작업한다.
t.Start();
}
위와 같이 코드를 수정한다.
Task 인스턴스를 생성할 때 TaskCreationOptions.LongRunning 을 인자로 넘겨주면, 스케줄러에서 부족한 쓰레드를 요청하여 작업할 수 있다.
위 사진과 같이, MainThread 함수가 원할히 실행됨을 확인할 수 있다.