이번 글에서는 C#에서 쓰레드 사용법에 대해 알아보겠습니다. 쓰레드는 말 그대로 타이머와 다르게 메인 쓰레드에 영향을 주지 않고 독립적인 쓰레드를 생성하여 하나의 프로그램에서 여러 작업을 동시에 처리할 수 있게 해주는 것을 말합니다. 여러 작업을 동시에 하기 때문에 사용할 때도 주의해서 사용해야 합니다. 여기서는 기본적인 사용 방법만 알아보겠습니다.
기본 함수
쓰레드를 사용하기 위한 기본 함수들은 아래와 같습니다.
- Start : 쓰레드 시작
- IsAlive : 쓰레드가 실행 중인지?
- Join : 쓰레드가 종료될 때까지 대기, Join에 파라미터 입력 시 입력한 시간 동안만 대기
- Interrupt : 쓰레드에 ThreadInterruptedException 발생
- Abort : 쓰레드 강제 종료. 사용되지 않음
- Suspend : 쓰레드 일시 중단. 사용되지 않음
- Resume : 쓰레드 다시 시작. 사용되지 않음
- Sleep : 파라미터에 입력한 시간만큼 쓰레드 일시 중단
쓰레드 생성
쓰레드를 생성하기 위해서 함수를 하나 생성해서 사용 할 수도 있고, 람다 표현식을 사용하여 생성 할 수 있습니다. 아래는 람다 표현식으로 쓰레드를 생성하는 코드입니다.
Thread th = new Thread(() =>
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Thread: {i}");
Thread.Sleep(1000); // 1초 Sleep
}
});
th.Start(); // 쓰레드 시작
혹은 람다를 사용하지 않고 아래와 같이 함수를 인자로 전달하여 사용하기도 합니다.
// 쓰레드 객체 생성은 아래와 같이 두개 모두 가능
Thread th = new Thread(Run);
Thread th = new Thread(new ThreadStart(Run));
th.Start(); // 쓰레드 시작
static void Run()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"Thread: {i}");
Thread.Sleep(1000);
}
}
쓰레드 시작 – Start
위 샘플 코드에서 보셨다시피 쓰레드를 시작하기 위해서는 Start 함수를 사용하면 됩니다. 콘솔 프로그램을 하나 생성해서 아래와 같이 쓰레드와 실행할 함수를 만들어주고 쓰레드를 시작해줍니다.
static void Main(string[] args)
{
// 생성
//Thread thread = new Thread(Run);
Thread thread = new Thread(new ThreadStart(Run));
thread.Start();
}
static void Run()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"Thread: {i}");
Thread.Sleep(1000);
}
}
실행하면 아래와 같이 표시합니다.
쓰레드 대기 – Join
Start에서 사용했던 쓰레드 예제는 아래 순서로 실행됩니다.
- 메인 시작
- 쓰레드 시작
- 메인 종료
- 쓰레드 종료
쓰레드가 종료되지도 않았는데 메인이 종료되는 모습을 볼 수 있습니다. 코드로 확인하기 위해 아래와 같이 Main의 시작과 끝, 쓰레드의 시작과 끝에 Console.WriteLine 함수를 추가하면 아래와 같이 표시합니다.
static void Main(string[] args)
{
Console.WriteLine("Start Main");
// 생성
Thread thread = new Thread(new ThreadStart(Run));
thread.Start();
Console.WriteLine("Close Main");
}
static void Run()
{
Console.WriteLine("Start Thread");
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"Thread: {i}");
Thread.Sleep(1000);
}
Console.WriteLine("Close Thread");
}
실제로 실행하면 쓰레드 전에 메인이 종료됐다고 하지만 어찌 됐던 메인이 종료되고도 쓰레드가 실행되고 있는게 문제가 됩니다. Main은 실행한 쓰레드를 정리를 하고 종료해야 합니다. 이때 Join을 사용하여 Main 쓰레드가 생성한 쓰레드를 종료할 때까지 대기하게 합니다.
Thread thread = new Thread(new ThreadStart(Run));
thread.Start(); // 쓰레드 시작
thread.Join(); // 종료할 때 까지 대기
Join 함수 파라미터에 아무런 값을 입력하지 않으면 쓰레드가 종료할 때 까지 대기하지만 파라미터를 입력하면 입력한 시간만큼 대기하게 됩니다.
thread.Join(3000); // 3초 대기
쓰레드 종료 – Interrupt
쓰레드에 ThreadInterruptedException 인터럽트를 발생 시켜 종료하는 방법은 Interrupt 함수를 사용하면 됩니다. 그리고 쓰레드에 try catch 문을 사용하여 ThreadInterruptedException을 캐치해야합니다.
Thread thread = new Thread(new ThreadStart(Run));
thread.Start();
if (thread.IsAlive)
{
Thread.Sleep(3000); // 3초 대기
thread.Interrupt(); // 인터럽트 발생
thread.Join(); // 종료할 때 까지 대기
}
static void Run()
{
Console.WriteLine("Start Thread");
try
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"Thread: {i}");
Thread.Sleep(1000);
}
}
catch (ThreadInterruptedException exInterrupt)
{
Console.WriteLine(exInterrupt.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.WriteLine("Close Thread");
}
이 코드를 실행하면 아래와 같이 표시됩니다.
쓰레드 종료 – Flag 사용
안전하게 쓰레드를 종료하기 위해 Flag도 많이 사용 됩니다. 변수로 선언하여 쓰레드 안에 조건문을 두어 참이 되면 쓰레드를 종료하는 방식입니다.
static bool bFlag = false; // 플래그 선언
Thread thread = new Thread(new ThreadStart(Run));
bFlag = true; // 시작 전 플래그 true 처리
thread.Start();
if (thread.IsAlive)
{
Thread.Sleep(3000); // 3초 대기
bFlag = false; // 종료를 위해 플래그 false 처리
thread.Join(); // 종료할 때 까지 대기
}
static void Run()
{
Console.WriteLine("Start Thread");
try
{
for (int i = 0; i < 10; i++)
{
if (bFlag == false) // false이 되면 for문 종료
break;
Console.WriteLine($"Thread: {i}");
Thread.Sleep(1000);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.WriteLine("Close Thread");
}
실행 하면 아래처럼 표시됩니다.
쓰레드에 파라미터 전달 – ParameterizedThreadStart
쓰레드에 파라미터를 전달하기 위해서는 쓰레드 생성 시 ParameterizedThreadStart로 생성하면 Start 함수 호출 시 파라미터를 전달 할 수 있습니다. 아래와 같이 for문 횟수를 인자로 받는 함수를 하나 생성해줍니다.
static void Run(object? iParamCount)
{
if (iParamCount != null)
{
int iCount = (int)iParamCount; // for 횟수
Console.WriteLine("param val: " + iCount);
for (int i = 0; i < iCount; i++)
{
Console.WriteLine($"Thread: {i}");
Thread.Sleep(1000);
}
}
}
그 다음 쓰레드 생성, 시작 시 아래와 같이 코딩해줍니다.
Thread thParam = new Thread(new ParameterizedThreadStart(Run));
thParam.Start(3);
thParam.Join();
실행 하면 아래와 같이 표시합니다.
Start에 5를 줬을 때랑 다르게 동작합니다.
github : https://github.com/3001ssw/c_sharp/tree/main/WinForm/Thread/ConsoleApp1