스레드의 생성
C#에서 쓰레드를 만드는 기본적인 클래스로 System.Threading.Thread라는 클래스가 있다. 이 클래스의 생성자(Constructor)에 실행하고자 하는 메서드를 델리게이트로 지정한 후, Thread클래스 객체에서 Start() 메서드를 호출하면 새로운 쓰레드가 생성되어 실행되게 된다. 아래 예는 동일 클래스 안의 Run() 메서드를 실행하는 쓰레드를 하나 생성한 후 실행시키는 예제이다. 예제에서는 기본적으로 생성된 메인 쓰레드에서도 동일하게 Run()메서드를 호출하고 있으므로, Begin/End문장이 2번 출력되고 있는데, 이는 2개의 쓰레드가 동시에 한 메서드를 실행하고 있기 때문이다.
namespace MultiThrdApp
{
using System;
using System.Threading;
class Program
{
static void Main(string[] args)
{
new Program().DoTest();
}
void DoTest()
{
// 새로운 쓰레드에서 Run() 실행
Thread t1 = new Thread(new ThreadStart(Run));
t1.Start();
// 메인쓰레드에서 Run() 실행
Run();
}
// 출력
// Thread#1: Begin
// Thread#3: Begin
// Thread#1: End
// Thread#3: End
void Run()
{
Console.WriteLine("Thread#{0}: Begin", Thread.CurrentThread.ManagedThreadId);
// Do Something
Thread.Sleep(3000);
Console.WriteLine("Thread#{0}: End", Thread.CurrentThread.ManagedThreadId);
}
}
}
C# 스레드 생성의 다양한 예제
Thread클래스의 생성자가 받아들이는 파라미터는 ThreadStart 델리게이트와 ParameterizedThreadStart 델리게이트가 있는데, 이 섹션은 파라미터를 직접 전달하지 않는 메서드들에 사용하는 ThreadStart 델리게이트 사용 예제를 보여준다. ThreadStart 델리게이트는 public delegate void ThreadStart();와 같이 정의되어 있는데, 리턴값과 파라미터 모두 void임을 알 수 있다. 따라서 파라미터와 리턴값이 없는 메서드는 델리게이트 객체로 생성될 수 있다. 아래 예에서 보이듯이, ThreadStart 델리게이트를 만족하는 다른 방식들 즉, 익명 메서드, 람다식 등도 모두 사용할 수 있다.
class Program
{
static void Main(string[] args)
{
// Run 메서드를 입력받아
// ThreadStart 델리게이트 타입 객체를 생성한 후
// Thread 클래스 생성자에 전달
Thread t1 = new Thread(new ThreadStart(Run));
t1.Start();
// 컴파일러가 Run() 메서드의 함수 프로토타입으로부터
// ThreadStart Delegate객체를 추론하여 생성함
Thread t2 = new Thread(Run);
t2.Start();
// 익명메서드(Anonymous Method)를 사용하여
// 쓰레드 생성
Thread t3 = new Thread(delegate()
{
Run();
});
t3.Start();
// 람다식 (Lambda Expression)을 사용하여
// 쓰레드 생성
Thread t4 = new Thread(() => Run());
t4.Start();
// 간략한 표현
new Thread(() => Run()).Start();
}
static void Run()
{
Console.WriteLine("Run");
}
}
다른 클래스 메서드
동일 클래스가 아닌 다른 클래스의 메서드를 쓰레드에 호출하기 위해서는 해당 클래스의 객체를 생성한 후 (혹은 외부로부터 전달 받은 후) 그 객체의 메서드를 델리게이트로 Thread에 전달하면 된다.
class Helper
{
public void Run()
{
Console.WriteLine("Helper.Run");
}
}
class Program
{
static void Main(string[] args)
{
// Helper클래스의 Run메서드 호출
Helper obj = new Helper();
Thread t = new Thread(obj.Run);
t.Start();
}
}
스레드 클래스 파라미터 전달
Thread 클래스는 파라미터를 전달하지 않는 ThreadStart 델리게이트와 파라미터를 직접 전달하는 ParameterizedThreadStart 델리게이트를 사용할 수 있다. ThreadStart 델리게이트는 public delegate void ThreadStart(); 프로토타입에서 알 수 있듯이, 파라미터를 직접 전달 받지 않는다.(물론 파라미터를 전달하는 방식은 있다. 아래 참조) ParameterizedThreadStart 델리게이트는 public delegate void ParameterizedThreadStart(object obj);로 정의되어 있는데, 하나의 object 파라미터를 전달하고 리턴 값이 없는 형식이다. 하나의 파라미터를 object 형식으로 전달하기 때문에, 여러 개의 파라미터를 전달하기 위해서는 클래스나 구조체를 만들어 객체를 생성해서 전달할 수 있다. 파라미터의 전달은 Thread.Start() 메서드를 호출할 때 파라미터를 전달한다. ThreadStart를 이용해 파라미터를 전달하는 방법은 일단 델리게이트 메서드는 파라미터를 받아들이지 않으므로 그 메서드 안에서 다른 메서드를 호출하면서 파라미터를 전달하는 방식을 사용할 수 있다. 이렇게 하면 파라미터를 아래 t3의 예처럼 여러 개 전달할 수도 있다.
class Program
{
static void Main(string[] args)
{
// 파라미터 없는 ThreadStart 사용
Thread t1 = new Thread(new ThreadStart(Run));
t1.Start();
// ParameterizedThreadStart 파라미터 전달
// Start()의 파라미터로 radius 전달
Thread t2 = new Thread(new ParameterizedThreadStart(Calc));
t2.Start(10.00);
// ThreadStart에서 파라미터 전달
Thread t3 = new Thread(() => Sum(10, 20, 30));
t3.Start();
}
static void Run()
{
Console.WriteLine("Run");
}
// radius라는 파라미터를 object 타입으로 받아들임
static void Calc(object radius)
{
double r = (double)radius;
double area = r * r * 3.14;
Console.WriteLine("r={0},area={1}", r, area);
}
static void Sum(int d1, int d2, int d3)
{
int sum = d1 + d2 + d3;
Console.WriteLine(sum);
}
}
Background 스레드 vs Foreground 스레드
Thread 클래스 객체를 생성한 후 Start()를 실행하기 전에 IsBackground 속성을 true/false로 지정할 수 있는데, 만약 true로 지정하면 이 쓰레드는 백그라운드 쓰레드가 된다. 디폴트 값은 false 즉 Foreground 쓰레드이다. 백그라운드와 Foreground 쓰레드의 기본적인 차이점은 Foreground 쓰레드는 메인 쓰레드가 종료되더라도 Foreground 쓰레드가 살아 있는 한, 프로세스가 종료되지 않고 계속 실행되고, 백그라운드 쓰레드는 메인 쓰레드가 종료되면 바로 프로세스를 종료한다는 점이다.
// Foreground 스레드
Thread t1 = new Thread(new ThreadStart(Run));
t1.Start();
// 백그라운드 스레드
Thread t2 = new Thread(new ThreadStart(Run));
t2.IsBackground = true;
t2.Start();
'프로그래밍 언어 > C#' 카테고리의 다른 글
C# _ Discard (변수 무시) (0) | 2023.06.07 |
---|---|
C# 오버플로우(Overflow), 언더플로우(Underflow) (0) | 2023.05.21 |
C# Task 클래스 (0) | 2023.03.24 |
C# foreach 사용법과 다양한 예제 (0) | 2023.01.08 |
C# 배열 복사 (0) | 2023.01.08 |