📄 멀티스레드
멀티스레드, 코어에서 여러 개의 스레드를 효과적으로 실행할 수 있는 하드웨어 지원을 갖추고 있는 장점을 활용하여 한번에 여러 개의 작업을 동시에 호출하여 실행하도록 하는 것입니다.
📑 Thread
namespace ServerCore
{
class Program
{
static void SubThread()
{
for (int i = 0; i < 10; ++i)
{
Console.WriteLine("서브 스레드");
}
}
static void Main(string[] args)
{
Thread subthread = new Thread(SubThread); //스레드 생성
subthread.Name = "서브스레드"; //스레드의 이름을 설정
subthread.IsBackground = true; //백그라운드 실행 (기본값은 false, true일경우 메인스레드가 종료되면 함께 종료)
subthread.Start(); //스레드 시작
Console.WriteLine("메인 Join 전");
subthread.Join(); //스레드(subthread)가 끝날때까지 기다림
Console.WriteLine("메인 Join 후");
}
}
}
Thread subthread = new Thread(SubThread); //스레드 생성
- 스레드를 생성합니다.
- 스레드에서 호출할 함수를 인자로 넣습니다.
subthread.Name = "서브스레드"; //스레드의 이름을 설정
- 생성한 스레드의 이름을 정합니다.
- 디버그에서 설정한 스레드의 이름을 확인할 수 있습니다.
subthread.IsBackground = true; //백그라운드 실행 (기본값은 false, true일경우 메인스레드가 종료되면 함께 종료)
namespace ServerCore
{
class Program
{
static void SubThread()
{
while(true)
{
Console.WriteLine("서브 스레드");
}
}
static void Main(string[] args)
{
Thread subthread = new Thread(SubThread); //스레드 생성
subthread.Name = "서브스레드"; //스레드의 이름을 설정
subthread.IsBackground = true; //백그라운드 실행 (기본값은 false, true일경우 메인스레드가 종료되면 함께 종료)
subthread.Start(); //스레드 시작
Console.WriteLine("메인 스레드");
}
}
}
- 생성한 스레드를 백그라운드로 실행할지 설정합니다.
- 기본값은 false이며, 메인스레드가 끝나도 서브스레드는 계속 실행됩니다.
- 하지만 true로 설정하면, 메인스레드가 끝날 때 서브스레드도 함께 종료됩니다.
subthread.Start(); //스레드 시작
- 스레드를 시작합니다.
subthread.Join(); //스레드(subthread)가 끝날때까지 기다림
- 호출한 서브스레드가 종료될 때까지 메인스레드는 현재 라인에서 기다립니다.
- 서브스레드가 무한루프상태라면 다음 라인으로 넘어갈 수 없습니다.
📑 ThreadPool
스레드 풀은, 오브젝트 풀링과 비슷하게 런타임도중 메모리에 미리 로드를 시켜놓고(호출 시 인스턴스를 새로 생성하지 않음) 필요시 현재 대기 상태인 대상을 찾아, 작업을 명령하도록 하는 방법입니다.
스레드를 생성하는 비용이 비싸기 때문에 ThreadPool을 사용하여 미리 생성해놓은 스레드를 찾아 호출하지만, 미리 생성해놓은 개수를 넘어가는 스레드가 필요한 경우 새로운 태스크를 실행할 수 없는 단점이 있습니다. ThreadPool은 비교적 짧은 수행시간을 가지는 테스트를 호출하는데 적합합니다.
namespace ServerCore
{
class Program
{
static void SubThread(object state)
{
for (int i = 0; i < 3; ++i)
{
Console.WriteLine("서브 스레드");
}
}
static void Main(string[] args)
{
ThreadPool.SetMinThreads(1, 1); //스레드 풀의 최소 개수 지정
ThreadPool.SetMaxThreads(5, 5); //스레드 풀의 최대 개수 지정, 스레드 호출이 최대 개수를 넘어가면 추가실행 불가능
for (int i = 0; i < 5; ++i) //스레드 5개를 모두 사용하기위한 예시(무한루프 스레드 호출)
{
ThreadPool.QueueUserWorkItem((obj) => { while (true) { } });
}
//서브스레드 호출
ThreadPool.QueueUserWorkItem(SubThread);
//스레드풀에서 호출되는 태스크는 IsBackground = true 상태이기때문에, 디버그를 위해서 메인스레드를 끝내지 않음
while (true) { }
}
}
}
- 스레드풀에서 대기상태인 스레드를 찾고, 해당 스레드에 원하는 함수를 호출합니다.
- 스레드풀에서 현재 대기상태인 스레드가 없다면, 호출되지 않습니다.
ThreadPool.SetMinThreads(1, 1); //스레드 풀의 최소 개수 지정
ThreadPool.SetMaxThreads(5, 5); //스레드 풀의 최대 개수 지정
- 스레드풀의 스레드 최소, 최대 개수를 설정합니다.
- 최대 개수를 넘어가는 호출이 발생한다면 해당 스레드는 호출되지 않습니다.
ThreadPool.QueueUserWorkItem(SubThread);
- 풀에서 대기상태의 스레드를 찾고, 호출합니다.
- 풀에서 호출되는 스레드는 기본적으로 IsBackground = true 속성처럼 실행되기에 메인스레드가 종료되면 서브스레드가 종료됩니다.
for (int i = 0; i < 5; ++i) //스레드 5개를 모두 사용하기위한 예시(무한루프 스레드 호출)
{
ThreadPool.QueueUserWorkItem((obj) => { while (true) { } });
}
//서브스레드 호출
ThreadPool.QueueUserWorkItem(SubThread);
- MaxThreads를 5로 설정하고 SubThread를 호출하지만, 이미 위에서 5개의 무한루프 스레드가 돌고 있기에 출력에서는 아무것도 나오지 않습니다.
for (int i = 0; i < 4; ++i) //스레드 5개를 모두 사용하기위한 예시(무한루프 스레드 호출)
{
ThreadPool.QueueUserWorkItem((obj) => { while (true) { } });
}
//서브스레드 호출
ThreadPool.QueueUserWorkItem(SubThread);
- 무한루프 스레드를 4개 호출하고 서브스레드를 호출하면 정상적으로 스레드호출이 됩니다.
📑 Task
namespace ServerCore
{
class Program
{
static void SubThread(object state)
{
for (int i = 0; i < 3; ++i)
{
Console.WriteLine("서브 스레드");
}
}
static void Main(string[] args)
{
ThreadPool.SetMinThreads(1, 1); //스레드 풀의 최소 개수 지정
ThreadPool.SetMaxThreads(5, 5); //스레드 풀의 최대 개수 지정, 스레드 호출이 최대 개수를 넘어가면 추가실행 불가능
for (int i = 0; i < 5; ++i) //스레드 5개를 모두 사용하기위한 예시(무한루프 스레드 호출)
{
//Task도 ThreadPool에서 관리되지만, 옵션을 이용하여 ThreadPool의 대기 스레드에 영향을 받지 않고 별도 처리가 가능
//TaskCreationOptions.LongRunning 옵션을 이용해 별도처리(오래 걸리는 처리)
Task task = new Task(() => { while (true) { } }, TaskCreationOptions.LongRunning);
task.Start();
}
//서브스레드 호출
ThreadPool.QueueUserWorkItem(SubThread);
//스레드풀에서 호출되는 태스크는 IsBackground = true 상태이기때문에, 디버그를 위해서 메인스레드를 끝내지 않음
while (true) { }
}
}
}
- Task도 마찬가지로 ThreadPool에 대기 스레드를 이용하여 호출되지만, 특별한 옵션을 통해 대기 스레드에 영향을 받지 않고 사용할 수 있는 장점이 있습니다. 긴 처리시간이 요구되는 프로세스를 호출할 때 적합할 수 있습니다.
TaskCreationOptions.LongRunning
- 태스크를 생성할 때 설정할 수 있는 옵션으로, LongRunning을 인자로 넣게 되면 ThreadPool의 워커스페이스를 사용하지 않고 호출할 수 있습니다.
- 즉, 본문에서 최대 5개로 설정한 Threads를 초과하여 호출할 수 있습니다.
for (int i = 0; i < 5; ++i) //스레드 5개를 모두 사용하기위한 예시(무한루프 스레드 호출)
{
Task task = new Task(() => { while (true) { } }, TaskCreationOptions.LongRunning);
task.Start();
}
//서브스레드 호출
ThreadPool.QueueUserWorkItem(SubThread);
- 다섯 개의 스레드를 호출하고 ThreadPool에서 서브스레드를 호출해도 호출이 정상적으로 이루어집니다.
for (int i = 0; i < 5; ++i) //스레드 5개를 모두 사용하기위한 예시(무한루프 스레드 호출)
{
Task task = new Task(() => { while (true) { } };
task.Start();
}
//서브스레드 호출
ThreadPool.QueueUserWorkItem(SubThread);
- 반대로 TaskCreationOptions.LongRunning를 사용하지 않고 호출하면 모든 스레드를 사용하고 있어 추가호출이 안됩니다.
'server > socket server' 카테고리의 다른 글
[C# 서버] Context Switching (0) | 2023.01.04 |
---|---|
[C# 서버] 스핀락 (0) | 2023.01.04 |
[C# 서버] 원자성, Interlocked, Monitor, lock (0) | 2023.01.02 |
[C# 서버] 메모리 배리어 (0) | 2023.01.02 |
[C# 서버] volatile (0) | 2022.12.30 |