C# 에서는 쓰레드 객체를 생성할 때, 기본적으로 Foreground 쓰레드로 실행됩니다. Main 메서드가 종료되어도 해당 쓰레드가 끝날 때까지 프로그램은 종료되지 않습니다.
Background 쓰레드 : Main 메서드가 종료되면 남아 있는 작업과 관계 없이 프로그램은 즉시 종료됩니다.isBackgorund 속성을 사용하여 해당 쓰레드를 Background 쓰레드로 변경할 수 있습니다.Thread t = new Thread(PrintThread); // 쓰레드 생성
t.IsBackground = true; // Background 쓰레드로 설정
t.Start(); // 쓰레드 시작
Console.WriteLine("Main");
void PrintThread()
{
Console.WriteLine("Print Thread");
}
결과
쓰레들 생성하는 건 비용이 매우 크기 때문에, 대신 쓰레드 풀을 사용하곤 합니다. 쓰레드 풀은 매번 새로운 쓰레드를 생성하지 않고, 미리 만들어진 쓰레드들을 재사용하여 생성 비용을 절감합니다. 짧고 반복적인 작업을 할 때 매우 유리합니다.
Thread Pool의 특징
Background : 기본적으로 Background이며, Main 종료 시 프로그램이 종료된다.ThreadPool 의 모든 쓰레드가 사용 중인 경우
ThreadPool.SetMinThreads(1, 1); // 최소 한 개의 쓰레드는 생성됨
ThreadPool.SetMaxThreads(5, 5); // 최대 다섯 개의 쓰레드만 생성됨
// 5개의 스레드 모두에게 무한 루프를 도는 작업 실행시키기
for (int i = 0; i < 5; i++)
{
ThreadPool.QueueUserWorkItem(ThreadPoolTest1);
}
**// Thread Pool로 돌아오는 쓰레드가 없으므로, 아래 쓰레드는 절대 실행되지 않는다.**
ThreadPool.QueueUserWorkItem(ThreadPoolTest2);
while(true) {} // 쓰레드풀은 Background이므로, Main이 종료되지 않기 위해 무한 루프 실행
// ===============================================
// Thread Pool의 쓰레드가 실행할 함수 1, 2
void ThreadPoolTest1(object state)
{
Console.WriteLine("양보하지 않는 쓰레드");
while (true) { }; // 쓰레드를 양보하지 않게 한다.
}
void ThreadPoolTest2(object state)
{
Console.WriteLine("ThreadPool");
}
결과
이 코드에서는 쓰레드 풀의 최대 개수를 5개로 설정하고, 5개의 쓰레드 모두에게 무한 루프를 도는 작업을 실행시켰습니다. 결국 작업을 마치고 쓰레드 풀로 돌아오는 쓰레드가 없게 되어, ThreadPoolTest2 는 영영 실행되지 않습니다.
이제 4개의 쓰레드에게만 ThreadPoolTest1을 실행시켜보겠습니다.
ThreadPool.SetMinThreads(1, 1); // 최소 한 개의 쓰레드는 생성됨
ThreadPool.SetMaxThreads(5, 5); // 최대 다섯 개의 쓰레드만 생성됨
// 4개의 스레드에게만 무한 루프를 도는 작업 실행시키기
for (int i = 0; i < 4; i++)
{
ThreadPool.QueueUserWorkItem(ThreadPoolTest1);
}
**// Thread Pool에 남은 하나의 쓰레드가 다음 작업을 실행한다.**
ThreadPool.QueueUserWorkItem(ThreadPoolTest2);
while(true) {} // 쓰레드풀은 Background이므로, Main이 종료되지 않기 위해 무한 루프 실행
// ===============================================
// Thread Pool의 쓰레드가 실행할 함수 1, 2
void ThreadPoolTest1(object state)
{
Console.WriteLine("양보하지 않는 쓰레드");
while (true) { }; // 쓰레드를 양보하지 않게 한다.
}
void ThreadPoolTest2(object state)
{
Console.WriteLine("ThreadPool");
}
결과
4개의 쓰레드는 ThreadPoolTest1을 실행하지만, 남은 하나의 쓰레드는 ThreadPoolTest2를 실행하는 걸 확인할 수 있습니다.
TaskCreationOptions.LongRunning : 쓰레드풀의 개수 제한을 넘어 작업을 수행하고 싶을 때TaskCreationOptions.LongRunninong
네트워크 요청, 파일 다운로드 등 긴 시간 동안 실행되는 작업에서 사용됩니다. ThreadPool 과 별개로 새로운 스레드를 생성하여 독립적으로 실행됩니다.
ThreadPool.SetMinThreads(1, 1); // 최소 한 개의 쓰레드는 생성됨
ThreadPool.SetMaxThreads(5, 5); // 최대 다섯 개의 쓰레드만 생성됨
// 쓰레드 풀과 독립적으로 실행될 Task
for (int i = 0; i < 5; i++)
{
Task t = new Task(() => { Console.WriteLine("Task"); }, TaskCreationOptions.LongRunning);
t.Start();
}
// 5개의 스레드 모두에게 Work를 실행시키기
for (int i = 0; i < 5; i++)
{
ThreadPool.QueueUserWorkItem(ThreadPoolTest1);
}
// 쓰레드풀은 Background이므로, Main이 종료되지 않기 위해 무한 루프 실행
while (true) { };
// ===============================================
// 쓰레드 풀이 실행할 함수 1, 2
void ThreadPoolTest1(object state)
{
Console.WriteLine("양보하지 않는 쓰레드");
while (true) { }; // 쓰레드를 양보하지 않게 한다.
}
결과
쓰레드 풀과는 독립적으로 실행 중인 Task를 확인할 수 있습니다.