Thread 생성
C#에서 Thread 생성하는 방법은 간단하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
static void thread1()
{
while(true)
Console.WriteLine("Hello Thread!");
}
static void Main(string[] args) // 메인 쓰레드
{
Thread t = new Thread(thread1); // 쓰레드 생성 및 작업(함수) 넘기기
t.Start(); // 쓰레드 시작
Console.WriteLine("Hello World!");
}
1
2
3
4
5
Hello World!
Hello Thread!
Hello Thread!
Hello Thread!
...
프로그램에서는 기본적으로 메인 쓰레드가 있고 위 코드에서는 쓰레드를 하나 더 생성해서 수행하는 것을 보여주고 있다.
또한 메인 쓰레드는 종료하더라도 다른 쓰레드가 남아있으면 프로그램이 종료되지 않는 것을 볼 수 있다. (background에서 실행시킬 경우 메인 쓰레드가 종료되면 프로그램도 종료된다.)
쓰레드를 생성할 때 여러 설정도 가능하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void thread1()
{
for(int i = 0; i < 5; i++)
Console.WriteLine("Hello Thread!");
}
static void Main(string[] args)
{
Thread t = new Thread(thread1); // 쓰레드 생성 및 작업 넘기기
t.Name = "Test Thread"; // 쓰레드 이름 지정(디버깅할때 확인해볼 수 있다)
t.IsBackground = true; // background에서 실행되도록 설정할 수 있다(기본 설정은 false)
t.Start(); // 쓰레드 시작
Console.WriteLine("Waiting for Thread!");
t.Join(); // 쓰레드가 종료될때까지 기다린다.
Console.WriteLine("Hello World!");
}
1
2
3
4
5
6
7
Waiting for Thread!
Hello Thread!
Hello Thread!
Hello Thread!
Hello Thread!
Hello Thread!
Hello World!
필요할 때 매번 쓰레드를 생성해서 관리해도 되긴 하지만 사실 쓰레드를 생성하는 것은 큰 부담이 되는 작업이다. 그렇기에 매번 쓰레드를 생성하지 않고 이전에 생성했던 것을 재사용하는 방법으로 ThreadPool을 활용해 볼 수 있다.
ThreadPool
ThreadPool은 쓰레드가 생성되고 작업을 끝낸 후에 사라지지 않고 Queue에 대기하고 있다가 또 작업이 들어왔을 때 그 작업을 들고가서 수행한다.
쓰레드를 매번 생성하고 지우는 것이 아니라 이미 생성해 놓은 쓰레드를 활용하는 것이기에 부담을 좀 줄일 수 있다. 또한 쓰레드 개수를 제한할 수 있기 때문에 쓰레드가 무한히 막 생성되는 것을 방지할 수 있다. 다만 오래걸리는 작업들을 수행시켰을 경우 쓰레드를 오랫동안 붙잡게 되어 쓰레드 부족으로 다른 작업들을 못하게 될 수도 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void thread1(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++)
ThreadPool.QueueUserWorkItem((obj) => { while(true){ } }); // 람다식을 이용해 일부러 끝나지 않는 작업을 넘김
ThreadPool.QueueUserWorkItem(thread1); // 쓰레드 부족으로 실행이 안됨
}
오래 걸리는 작업일 경우 ThreadPool에서 쓰레드를 사용하지 않고 따로 생성해서 관리하는 것이 효율적일 수 있다.
사실 이러한 점들을 효율적으로 관리할 수 있는 방법으로 Task가 있다.
Task
Thread와 사용법이 비슷하며 기본적으로 ThreadPool에서 쓰레드를 가져와 작업을 수행한다. 다만 오래 걸릴 것같은 작업은 따로 생성해서 관리할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static void thread1(object state)
{
for(int i = 0; i < 5; i++)
Console.WriteLine("Hello Thread!");
}
static void Main(string[] args)
{
ThreadPool.SetMinThreads(1, 1);
ThreadPool.SetMaxThreads(5, 5);
for (int i = 0; i < 5; i++)
{
// Task도 ThreadPool에서 쓰레드를 가져오지만
// 오래걸릴 것같은 작업은 이렇게 따로 지정해놓으면 Task에서 따로 관리한다.
Task t = new Task(() => { while (true) { } }, TaskCreationOptions.LongRunning);
t.Start();
}
ThreadPool.QueueUserWorkItem(thread1); // 실행되는 것을 볼 수 있다.
while (true) { } // 메인쓰레드가 종료되지 않도록
}
1
2
3
4
5
Hello Thread!
Hello Thread!
Hello Thread!
Hello Thread!
Hello Thread!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void thread1(object state)
{
for(int i = 0; i < 5; i++)
Console.WriteLine("Hello Thread!");
}
static void Main(string[] args)
{
ThreadPool.SetMinThreads(1, 1);
ThreadPool.SetMaxThreads(5, 5);
for (int i = 0; i < 5; i++)
{
Task t = new Task(() => { while (true) { } });
t.Start();
}
ThreadPool.QueueUserWorkItem(thread1); // 실행되지 않는다
while (true) { } // 메인쓰레드가 종료되지 않도록
}
C#에서 보통 쓰레드를 직접생성해서 관리할 일는 거의 없고 ThreadPool 개념과 Task만 잘 사용해도 충분하다.