Thread
Thread 생성
using System;
using System.Threading; // 추가해줘야 사용 가능
namespace ServerCore
{
public class Program
{
// 사용할 함수(쓰레드)
static void MainThread()
{
Console.WriteLine("Create Thread");
}
static void Main(string[] args)
{
//Thread 이름 = new Thread(함수 이름);
Thread thread = new Thread(MainThread);
thread.Start();
}
}
}
- using System.Threading을 추가해 줘야 사용이 가능하다.
- thread.Start()를 해야 만들어둔 thread가 실행된다.
- thread는 Main과 별도로 실행되는 것
foreground vs background
using System;
using System.Threading;
namespace ServerCore
{
public class Program
{
static void MainThread()
{
//MainThread에 출력 메시지를 무한루프 돌린다고 가정
while(true) Console.WriteLine("Create Thread");
}
static void Main(string[] args)
{
Thread thread = new Thread(MainThread);
// false = foreground
// true = background
thread.IsBackground = true;
thread.Start();
Console.WriteLine("ThisIsMain");
}
}
}
- 기본적으로 thread를 생성하면 foreground로 생성됨
- IsBackgrond 프로퍼티로 설정 가능
- false = foreground
- true = background
foreground
: Main이 종료돼도 스레드가 종료되지 않고 살아있음- Main이 종료돼도 끝나지 않고 계속 생성됨
- Main이 종료돼도 끝나지 않고 계속 생성됨
background
: Main이 종료되면 스레드도 같이 종료됨- ThisIsMain이 호출되고 얼마 되지 않아 CreateThread도 종료됨
- ThisIsMain이 호출되고 얼마 되지 않아 CreateThread도 종료됨
Join
- Thread를 호출한 함수가 Thread의 종료시까지 기다리는 것
public class Program
{
static void MainThread()
{
for(int i = 0; i < 5; i++) Console.WriteLine("Create Thread");
}
static void Main(string[] args)
{
Thread thread = new Thread(MainThread);
// false = foreground
// true = background
thread.IsBackground = true;
thread.Start();
Console.WriteLine("Main Is Waiting");
thread.Join(); // MainThrea의 종료까지 기다
Console.WriteLine("System End");
}
}
}
ThreadPool
- Thread를 직접 만들어서 관리하는 것은 시스템적으로 부담이 큰 일
- C#에서 이미 만들어져 있는 Thread를 미리 빌려와서 사용 후 다시 반환
→ 쓰레드를 만드는 것보다 부담이 훨씬 적음
주요 메소드 및 프로퍼티
ThreadPool.QueueUserWorkItem(함수이름)
- 쓰레딩하고자 하는 함수를 동작시킴 (Thread의 start()와 비슷)
- 이때 해당 함수는 매개변수로 object 혹은 object? 클래스형을 가져야 함
ThreadPool.SetMinThreads(스레드 개수, 비동기 I/O Thread 개수)
- 최소로 빌려올 수 있는 스레드 개수
- 최소로 빌려올 수 있는 스레드 개수
ThreadPool.SetMaxThreads(스레드 개수, 비동기 I/O Thread 개수)
- 최대로 빌려올 수 있는 스레드 개수
ThreadPool 사용 시 주의할 점
public class Program
{
static void MainThread(object? obj)
{
for(int i = 0; i < 5; i++) Console.WriteLine("Create Thread");
}
static void Main(string[] args)
{
// 쓰레드를 최소 1개 최대 3개까지밖에 못빌려주도록 제한
ThreadPool.SetMinThreads(1, 1);
ThreadPool.SetMaxThreads(3, 3);
//3번 반복
for(int i = 0; i < 3; i++)
// 람다식으로 자원을 한번 잡으면 영원히 반환하지 않는 함수를 만듦
ThreadPool.QueueUserWorkItem((obj) => { while (true) ; });
// 쓰레드 풀 사용
ThreadPool.QueueUserWorkItem(MainThread);
while (true) ;
}
}
- Thread Pool은 자원 공유 → 대기 → 반환 → 공유 → 대기 → 반환을 반복한다.
- Thread Pool에 남아있는 자원이 없다면 더 이상 스레드를 생성할 수 없다.
- 위와 같은 경우에선 이미 3개의 스레드가 실행되고 있기 때문에 4번째 스레드를 생성하지 못한다.
- Thread Pool 에는 짧은 함수만을 넣는 것이 좋음
- 이를 해결하는 방법으로 Task를 사용
Task
public class Program
{
static void MainThread(object? obj)
{
for(int i = 0; i < 5; i++) Console.WriteLine("Create Thread");
}
static void Main(string[] args)
{
// 쓰레드를 최소 1개 최대 3개까지밖에 못빌려주도록 제한
ThreadPool.SetMinThreads(1, 1);
ThreadPool.SetMaxThreads(3, 3);
for (int i = 0; i < 3; i++)
{
// Task의 LongRunning 옵션 사용
Task t = new Task(() => { while (true) ; }, TaskCreationOptions.LongRunning);
t.Start();
}
ThreadPool.QueueUserWorkItem(MainThread);
while (true) ;
}
}
- Task를 생성하면서 TaskCreationOptions.LongRunning 옵션을 사용한다.
- Task도 기본적으로 ThreadPool에서 돌아가지만
LongRunning 옵션을 생성해 둔 것은 별도의 스레드를 생성한다.