Q1. t라는 스레드를 생성하고, Hello Thread를 출력하는 MainThread라는 함수를 넣어 실행해 보자. Main 함수에는 Hello World를 출력해보세요.
Q2. Background의 기본 상태는 무엇인가? Background=true로 설정한 뒤 thread가 끝날까지 Main Thread가 종료되지 않고 기다리게 해보세요.
Q3. 스레드의 이름을 No Name에서 원하는 이름으로 바꿔 보세요.
Q4. ThreadPool을 이용하여 MainThread를 실행해 보세요
Q5. new Thread를 이용한 쓰레드와 ThreadPool을 이용한 쓰레드의 차이점을 3가지 말해 보세요.
Q6. 사용 가능 ThreadPool의 개수를 1~5개로 제한해 보세요.
Q7. 쓰레드를 5개로 제한하고 while(true)를 ThreadPool로 5번 반복하면 먹통이 된다. Task를 사용해 이를 해결해 보세요. Q8. 정직원, 단기 알바 파견 업체, 프로젝트 단위 계약직에 빗대어 설명해 보세요.
정직원 쓰레드 생성
ServerCore에서
using System;
using System.Threading;
namespace ServerCore
{
internal class Program
{
**static void MainThread()
{
Console.WriteLine("Hello Thead!");
}**
static void Main(string[] args)
{
**Thread t = new Thread(MainThread);
t.Start();**
* ***Console.WriteLine("Hello World!");**
}
}
}
이렇게 하면
C++과 다른 C#에서의 Background 기본 상태
using System;
using System.Threading;
namespace ServerCore
{
internal class Program
{
**static void MainThread()
{
while(true)
Console.WriteLine("Hello Thead!");
}**
static void Main(string[] args)
{
**Thread t = new Thread(MainThread);
t.Start();
Console.WriteLine("Hello World!");**
}
}
}
이렇게 하면 “Hello Thread”가 무한히 출력되고 끝나지 않는다.
C++과 달리 기본적으로
t.IsBackground = false;
인 상태다.
t.IsBackground = true;
라고 하면 백그라운드에서 실행이 되는거니까 main이 종료되면 thread가 실행되건 말건 종료한다.
t.Join();
을 하면 thread가 끝날 때 까지 기다렸다가 다음 걸 출력 하겠다는 의미다.
using System;
using System.Threading;
namespace ServerCore
{
internal class Program
{
static void MainThread()
{
while(true)
Console.WriteLine("Hello Thead!");
}
static void Main(string[] args)
{
Thread t = new Thread(MainThread);
**t.IsBackground = true;**
t.Start();
Console.WriteLine("Waiting for Thread World!");
**t.Join();**
Console.WriteLine("Hello World!");
}
}
}
다시 무한으로 출력된다.
모두 중지를 누르고 보면 main thread는 t.join()에 멈춰있고,
다른 스레드는 Console.WriteLine("Hello Thead!");에 멈춰 있다
스레드의 이름을 지정하는 방법
이 스레드는 No Name이라 되어 있는데 이름을 지정할 수 있다.
t.Name = "Test Thread";
이렇게 지정하면 된다.
이렇게 나온다.
직원이 각각 어떤일 하는지 보고 싶으면 이렇게 왔다 갔다 하면 된다.
단기 알바 쓰레드
new Thread했다는 건 정직원을 채용했다는 말
쓰레드를 이렇게 무한으로 평생 챙겨주는 정직원이 아니라 단기알바로 잠깐 쓰는 경우를 생각해 보자.
using System;
using System.Threading;
namespace ServerCore
{
internal class Program
{
static void MainThread(object state)
{
for(int i=0; i<5; i++)
Console.WriteLine("Hello Thead!");
}
static void Main(string[] args)
{
**ThreadPool.QueueUserWorkItem(MainThread);**
//Thread t = new Thread(MainThread);
//t.Name = "Test Thread";
//t.IsBackground = true;
//t.Start();
//Console.WriteLine("Waiting for Thread World!");
//t.Join();
//Console.WriteLine("Hello World!");
}
}
}
실행하면 바로 종료한다.
ThreadPool 얘를 사용하면 Background로 돌아가는 쓰레드 라는 걸 추측할 수 있다.
없어지지 않게 하기 위해 while 문을 돌게 한다.
using System;
using System.Threading;
namespace ServerCore
{
internal class Program
{
static void MainThread(object state)
{
for(int i=0; i<5; i++)
Console.WriteLine("Hello Thead!");
}
static void Main(string[] args)
{
ThreadPool.QueueUserWorkItem(MainThread);
//Thread t = new Thread(MainThread);
//t.Name = "Test Thread";
//t.IsBackground = true;
//t.Start();
//Console.WriteLine("Waiting for Thread World!");
//t.Join();
//Console.WriteLine("Hello World!");
while(true)
{
}
}
}
}
실행하면 5번 메시지가 뜬다.
단기알바로 고용한 애가 일을 잘 수행했다는 걸 알 수 있다.
TheradPool을 이용하면 직원들이 이미 다 마련이 되어 있는 거. 이미 4대보험 들고 고용되어 있는 상태의 유동적으로 기다리고 있는 직원을 일 시키는 거. 끝나면 다시 대기소에 가서 기다리는 상태 되는 거. 필요할 때 마다 직원 고용하는 게 아니 대기 중인 직원 일 시키는 거. 이걸 Pooling이라 해.
유니티에서도 ObjectPool이 있었어. 같은 개념. ThreadPool.
또 다른 차이는 Thread는 갯수 제한이 없어.
근데 스레드가 너무 많아지면 Thread마다 빙의시키는 왔다 갔다 하는 작업에 더 힘을 많이 쓸 수 있어. 문제가 되는 방법
ThreadPool을 사용하는 방법은 동시에 돌릴 갯수를 제한을 하기 때문에 갯수를 늘려도 기존에 작업하던 알바들이 일을 끝내고 돌아 올 때 다시 재투입한다. 인력 사무소 같은 개념. 좋은 걸 수도 있지만 일이 무한대면 영영 돌아오지 않아. 인력 부족해서 실행 못하는 상황이 올 수도 있다. ThreadPool 쓸 때는 가급적 짧은 일감을 던지는게 좋다.
using System;
using System.Threading;
namespace ServerCore
{
internal class Program
{
static void MainThread(object state)
{
for(int i=0; i<5; i++)
Console.WriteLine("Hello Thead!");
}
static void Main(string[] args)
{
**ThreadPool.SetMinThreads(1**, 1);// 최소 1개, 두번째 인자는 나중에 Input Output과 관련된 거라 스킵한다.Network 이벤트를 기다리거나 하는 스레드의 개수를 설정하는거.
**ThreadPool.SetMaxThreads(5**, 5);// 최대 5개 스레드라 설정
for (int i = 0; i < 5; i++)
ThreadPool.QueueUserWorkItem((obj) => { while (true) { } });
// 람다식 obj를 받아서 무엇무엇인가 하는 애로 만들어 주세요.
ThreadPool.QueueUserWorkItem(MainThread);
//Thread t = new Thread(MainThread);
//t.Name = "Test Thread";
//t.IsBackground = true;
//t.Start();
//Console.WriteLine("Waiting for Thread World!");
//t.Join();
//Console.WriteLine("Hello World!");
while(true)
{
}
}
}
}
이렇게 하면 MainThread를 실행해달라고 요청을 하더라도 5개 쓰레드를 계속 사용하고 있기 때문에 먹통이 된다.
ctrl+f5로 실행을 해보면 아무것도 출력이 되지 않는다.
for (int i = 0; i < 4; i++)
ThreadPool.QueueUserWorkItem((obj) => { while (true) { } });
4로 바꾸면 하나의 쓰레드가 남기 때문에 HelloThread!가 출력된다.
ThreadPool의 단점을 극복할 방법 Task
이런 ThreadPool의 단점을 극복할 한가지 방법이 더 있다.
Task는 직원이 할 일감 단위를 정의해서 사용한다는 의미
Task t = new Task(() => { while (true) { } }, TaskCreationOptions.LongRunning);
ThreadPool에 들어가긴 하는데 오래 걸리는 작업이 될 것이다 알려주는 거.
ThreadPool에서 뽑아서 실행하는 게 아니라 별도 처리를 해주는 거.
Thread와 ThreadPool의 양쪽 장점만을 이용했다고 볼 수 있다.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ServerCore
{
internal class Program
{
static void MainThread(object state)
{
for(int i=0; i<5; i++)
Console.WriteLine("Hello Thead!");
}
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) { } }, TaskCreationOptions.LongRunning);
// Task인자 타입이 action이라서 아무것도 안받는 형태
t.Start();**
}
//for (int i = 0; i < 4; i++)
// ThreadPool.QueueUserWorkItem((obj) => { while (true) { } });
ThreadPool.QueueUserWorkItem(MainThread);
//Thread t = new Thread(MainThread);
//t.Name = "Test Thread";
//t.IsBackground = true;
//t.Start();
//Console.WriteLine("Waiting for Thread World!");
//t.Join();
//Console.WriteLine("Hello World!");
while(true)
{
}
}
}
}
이렇게 하면 정상적으로 Hello Thread! 5개 출력이 실행이 된다.
오래 걸리는 task는 인자를 추가해서 사용할 수 있을거고
만약 TaskCreationOptions.LongRunning 인자를 뺀다면,
아까와 마찬가지로 아무것도 뜨지 않는 먹통이 된 상태가 된다.
5명을 다 추출해서 사용하고 있는 상태가 된거고, 오래 사용할 것인지 아닌지에 따라가지고 ThreadPool을 효율적으로 관리할 수 있다는 얘기가 된다.
C#에서는 Thread를 직접 관리할 일이 거의 없다고 보면 된다. ThreadPool에서 제공하는 기능들을 최대한 활용하면 좋고, 오래 걸리는 작업의 경우는 굳이 Thread를 만들지 않고 Task를 만들어서 사용하면 충분한다.
직원을 고용을 하고, 정직원 아닌 알바 고용할 수 있고, 인력 사무소에서 직원을 고용하는 작업까지 한뻔씩 들러 봤다.
ThreadPool : 알바, 인력 사무소
new Thread : 정직원
Task : 둘의 장점만 이용
'Server programming' 카테고리의 다른 글
01_04_멀티쓰레드_캐시이론 (0) | 2023.03.12 |
---|---|
01_03_멀티쓰레드_컴파일러 최적화 (0) | 2023.03.12 |
01_02_멀티쓰레드_쓰레드 생성(구버전) (0) | 2023.03.12 |
01_01_멀티쓰레드 개론 (0) | 2023.03.12 |
00_03_OT_환경 설정 (0) | 2023.03.12 |
댓글