멀티 쓰레드
정직원 쓰레드 생성
using System.Threading;
class Program
{
static void MainThread(object state)
{
Console.WriteLine("Hello, Thread!");
}
// 메인 직원
static void Main(string[] args)
{
// 직원을 한명 더 고용하고, 일을 시킨다.
Thread t = new Thread(MainThread);
t.Start();
Console.WriteLine("Hello, World!");
}
}
쓰레드를 생성하고 초기화 하는 것은 어렵지 않다. Thread t = new Thread()로 새로운 쓰레드를 만들어준다. 새로운 메서드를 MainThread라고 만들어주고 생성한 쓰레드 안에 매개변수로 넣어주면 된다.
따라서 Main 함수가 메인 직원이라면, Thread는 새로운 직원을 한명 더 고용하는 개념이고, MainThread는 새로운 직원이 해야 할 일이라고 볼 수 있다.
쓰레드의 백그라운드, 포그라운드 설정
using System.Threading;
class Program
{
static void Main(string[] args)
{
// 직원을 한명 더 고용하고, 일을 시킨다.
Thread t = new Thread(MainThread);
t.Start();
// 스레드를 백그라운드 스레드 또는 포그라운드 스레드로 만들지 결정한다.
// true = 백그라운드 스레드, false = 포그라운드 스레드
t.IsBackground = true;
Console.WriteLine("Hello, World!");
}
}
쓰레드의 종료 시점은 Main 함수가 종료되는 시점과 같다. 그렇지만 백그라운드, 포그라운드 스레드의 여부에 따라 종료 시점을 달리 설정할 수 있다. IsBackground을 true, false로 바꿔가며 실습해보자.
쓰레드의 종료 확인하기
using System.Threading;
class Program
{
static void Main(string[] args)
{
// 직원을 한명 더 고용하고, 일을 시킨다.
Thread t = new Thread(MainThread);
t.Start();
// 스레드를 백그라운드 스레드 또는 포그라운드 스레드로 만들지 결정한다.
// true = 백그라운드 스레드, false = 포그라운드 스레드
t.IsBackground = true;
Console.WriteLine("Waiting for Thread");
// 백그라운드 스레드의 종료를 기다린다.
t.Join();
Console.WriteLine("Hello, World!");
}
}
Join 메서드를 이용하면 스레드의 종료 시점을 확인할 수 있다. 디버깅을 통해 메인 쓰레드와 새로 생성한 쓰레드의 위치를 확인할 수 있다. 디버깅을 정지하고 스레드 탭을 누르면 주 스레드와 새로 생성한 스레드를 확인할 수 있다.
// 스레드의 이름을 설정한다
t.Name = "Test Thread";
또한 방금 생성한 쓰레드의 이름을 설정할 수 있다. 위와 같이 코드를 추가하면 우리가 테스트 하려는 쓰레드의 이름을 설정하여 보다 쉽게 찾을 수 있다. 😎
알바 형식의 쓰레드
정직원 쓰레드를 뽑기에는 비용이 너무 크고, 정직원 월급을 꼬박꼬박 주기가 힘들 수 있다. 따라서 인력사무소에서 단기 알바를 뽑듯이 쓰레드를 사용할 수 있다. 그것이 바로 ThreadPool 기능이다. ThreadPool은 이미 직원들이 고용되어 있는 형태이므로 쓰레드를 새로 생성할 필요가 없어 비용 절감의 효과를 누릴 수 있다.
using System.Threading;
class Program
{
static void Main(string[] args)
{
ThreadPool.SetMinThreads(1, 1);
ThreadPool.SetMaxThreads(5, 5);
⭕
for (int i = 0; i < 4; i++)
{
ThreadPool.QueueUserWorkItem((obj) => { while (true) { } });
}
ThreadPool.QueueUserWorkItem(MainThread);
}
}
SetMinThreads(인자 값1, 인자 값2)
인자 값 1은 최소로 일을 할 아이의 개수를 설정한다.
인자 값 2는 I/O 즉. 인풋, 아웃풋과 관련된 작업의 개수를 설정한다. 주로 네트워크단에서 활용한다.
SetMaxThreads(인자 값1, 인자 값2)
마찬가지로 인자 값 1은 최대로 일을 할 아이의 개수를 설정한다.
인자 값 2는 위와 같다.
그럼 그냥 쓰레드를 많이 생성하면 되지!
그렇지 않다. 이전 강의에서도 말한 것과 같이 1000개의 쓰레드를 생성한다고 해서 효율이 1000배 좋아지는 것이 아니다. 오히려 코어 수를 맞추는 것이 더 중요하다. 쓰레드가 너무 많아지면 각 쓰레드의 작업 실행 속도보다 쓰레드가 일을 하기 위해 왔다 갔다 하는 작업이 더 오래 걸릴 수 있다.
using System.Threading;
class Program
{
static void Main(string[] args)
{
❌
for (int i = 0; i < 1000; i ++)
{
Thread t = new Thread(MainThread);
t.IsBackground = true;
t.Start();
}
}
}
쓰레드 풀 먹통시키기
풀을 이용하여 쓰레드를 통해 일을 시킨다면 쓰레드를 1000개 생성하는 것이 아닌 SetMaxThreads 설정한 쓰레드의 개수만큼 일을 했던 쓰레드들이 일을 마치고 다시 돌아가 일을 하는 개념이다. 하지만 이 때, while 문을 통해 무한루프에 걸리게 된다면 쓰레드가 일을 마치고 다시 돌아올 수 없기 때문에 프로그램이 터질 수 있다. 따라서 관리에 유의하여야 한다. 즉. 간단한 작업을 이용할 때에는 풀을 쓰면 좋다.
using System.Threading;
class Program
{
static void Main(string[] args)
{
ThreadPool.SetMinThreads(1, 1);
ThreadPool.SetMaxThreads(5, 5);
❌ 쓰레드풀이 돌아오지 못한다..
for (int i = 0; i < 5; i++)
{
ThreadPool.QueueUserWorkItem((obj) => { while (true) { } });
}
ThreadPool.QueueUserWorkItem(MainThread);
}
}
따라서 이 모든 단점을 극복할 수 있는 개념이 바로 Task이다.
Task
직원을 고용하는 것이 아닌 직원이 할 일감 단위를 정의하는 것. 쓰레드와 마찬가지로 생성하고 초기화하여 사용할 수 있다.
using System.Threading;
class Program
{
static void Main(string[] args)
{
ThreadPool.SetMinThreads(1, 1);
ThreadPool.SetMaxThreads(5, 5);
for (int i = 0; i < 5; i++)
{
// Task 생성
Task ta = new Task(() => { while (true) { } }, TaskCreationOptions.LongRunning);
ta.Start();
}
ThreadPool.QueueUserWorkItem(MainThread);
}
}
Task도 마찬가지로 무한 루프를 돌 수 있지만 TaskCreationOptions.LongRunning 메서드를 사용하면 작업이 오래 걸린다는 의미를 넘겨 받아 별도의 처리를 진행하여 무한 루프에 돌지 않고 작업을 완료해낸다. 만약 적지 않는다면 아까 풀을 사용한 것과 같이 작업이 무한루프에 빠지게 된다. 😥 따라서 오래걸리는 일은 궂이 쓰레드를 만드는 것이 아닌 Task를 이용하여 사용하는 것이 좋다.
'공부 > 인프런 - Rookiss' 카테고리의 다른 글
Part 4-2-4. 멀티쓰레드 프로그래밍 : 캐시 이론 (1) | 2023.09.06 |
---|---|
Part 4-2-3. 멀티쓰레드 프로그래밍 : 컴파일러 최적화 (0) | 2023.09.06 |
Part 4-2-1. 멀티쓰레드 프로그래밍 : 멀티쓰레드 개론 (0) | 2023.09.04 |
Part 4-1-1. 서버 OT 및 환경 설정 (1) | 2023.09.03 |
Part 4. 게임 서버 (0) | 2023.09.03 |