방법: 스레드 만들기 및 종료(C# 프로그래밍 가이드)
이 예제에서는 보조 또는 작업자 스레드를 만들고 기본 스레드와 함께 이 스레드를 병렬로 사용하여 작업 처리를 수행하는 방법을 보여 줍니다. 또한 다른 스레드의 작업이 끝날 때까지 한 스레드가 대기하도록 만들고 스레드를 올바르게 종료하는 방법도 보여 줍니다. 다중 스레딩에 대한 배경 정보는 관리되는 스레딩 및 스레딩 사용(C# 프로그래밍 가이드)을 참조하십시오.
이 예제에서는 Worker
라는 클래스를 만듭니다. 이 클래스에는 호출된 DoWork
를 작업자 스레드가 실행하는 데 사용하는 메서드가 포함됩니다. 이는 본질적으로 작업자 스레드의 Main
함수입니다. 작업자 스레드는 이 메서드를 호출하여 실행을 시작하고 이 메서드가 반환될 때 자동으로 종료됩니다. DoWork
메서드는 다음과 같습니다.
public void DoWork()
{
while (!_shouldStop)
{
Console.WriteLine("worker thread: working...");
}
Console.WriteLine("worker thread: terminating gracefully.");
}
Worker
클래스에는 DoWork
에 반환할 시기를 알리는 데 사용되는 추가 메서드가 포함됩니다. RequestStop
이라는 이 메서드는 다음과 같습니다.
public void RequestStop()
{
_shouldStop = true;
}
RequestStop
메서드는 _shouldStop
데이터 멤버를 true에 할당하기만 합니다. 이 데이터 멤버는 DoWork
메서드에서 검사하므로 이는 DoWork
의 반환을 통해 작업자 스레드를 종료하는 결과를 가져옵니다. 그러나 중요한 점은 DoWork
및 RequestStop
이 서로 다른 스레드에서 실행된다는 사실입니다. DoWork
는 작업자 스레드에서 실행되고 RequestStop
은 기본 스레드에서 실행되므로 _shouldStop
데이터 멤버는 다음과 같이 volatile로 선언됩니다.
private volatile bool _shouldStop;
volatile 키워드는 여러 스레드가 _shouldStop
데이터 멤버에 액세스하므로 이 멤버의 상태에 대한 최적화 가정을 하지 말아야 한다는 사실을 컴파일러에 경고로 알립니다. 자세한 내용은 volatile(C# 참조)을 참조하십시오.
_shouldStop
이 bool인 경우 volatile을 _shouldStop
데이터 멤버와 함께 사용하면 형식 스레드 동기화 기술을 사용하지 않고도 여러 스레드에서 이 멤버에 안전하게 액세스할 수 있습니다. 즉, 한 번의 단일 원자 연산만으로 _shouldStop
을 수정할 수 있습니다. 그러나 이 데이터 멤버가 클래스, 구조체 또는 배열인 경우 여러 스레드에서 이 멤버에 액세스하면 간헐적으로 데이터가 손상될 수 있습니다. 배열의 값을 변경하는 스레드를 생각해 볼 수 있습니다. Windows에서는 다른 스레드를 실행할 수 있도록 스레드를 정기적으로 중단하므로 일부 배열 요소는 할당했지만 다른 요소를 아직 할당하지 않은 상태에서 이 스레드가 중단될 수 있습니다. 이 경우 배열의 상태는 프로그래머가 의도한 것과 전혀 다르므로 이 배열을 읽는 다른 스레드의 작업이 실패할 수 있습니다.
작업자 스레드를 실제로 만들기 전에 Main
함수에서 Worker
개체와 Thread의 인스턴스를 만듭니다. 스레드 개체는 Worker.DoWork
메서드에 대한 참조를 다음과 같이 Thread 생성자에 전달하여 이 메서드를 진입점으로 사용하도록 구성됩니다.
Worker workerObject = new Worker();
Thread workerThread = new Thread(workerObject.DoWork);
이 시점에서 작업자 스레드 개체가 존재하고 구성된 상태이지만 실제 작업자 스레드는 아직 작성되지 않습니다. 실제 작업자 스레드는 Main
에서 Start 메서드를 호출해야 작성됩니다.
workerThread.Start();
이제 시스템에서 작업자 스레드의 실행을 초기화하지만 이 과정은 기본 스레드에 대해 비동기적으로 수행됩니다. 즉, Main
함수는 작업자 스레드를 동시에 초기화하는 동안 코드를 계속하여 즉시 실행합니다. 작업자 스레드가 실행되기도 전에 Main
함수가 이 스레드를 종료하지 않도록 하기 위해 Main
함수는 작업자 스레드 개체의 IsAlive 속성이 true로 설정될 때까지 반복됩니다.
while (!workerThread.IsAlive);
그런 다음 Sleep을 호출하여 기본 스레드가 잠시 중단됩니다. 이렇게 하면 Main
함수가 다른 명령을 실행하기 전에 작업자 스레드의 DoWork
함수에서 DoWork
메서드 안의 루프를 몇 차례 반복하여 실행할 수 있습니다.
Thread.Sleep(1);
1밀리초가 경과하면 Main
은 앞서 가져온 Worker.RequestStop
메서드를 사용하여 작업자 스레드 개체를 종료하도록 신호를 보냅니다.
workerObject.RequestStop();
Abort를 호출하여 다른 스레드에서 스레드를 종료할 수도 있지만 대상 스레드를 이와 같이 강제로 종료하면 해당 스레드의 작업이 완료되었는지 확인할 수 없을 뿐 아니라 리소스를 정리할 수도 없습니다. 따라서 이 예제에서 설명하는 방법을 사용하는 것이 더 좋습니다.
마지막으로, Main
함수가 작업자 스레드 개체에 대한 Join 메서드를 호출하니다. 이 메서드는 개체가 가리키는 스레드가 종료될 때까지 현재 스레드를 차단하거나 대기 상태로 만듭니다. 따라서 Join은 작업자 스레드가 반환되고 자체 종료될 때까지 반환되지 않습니다.
workerThread.Join();
이 단계에서는 Main
을 실행하는 기본 스레드만 남게 됩니다. 이 스레드는 최종 메시지 하나를 표시한 다음 반환되어 기본 스레드를 함께 종료합니다.
완성된 예제는 다음과 같습니다.
예제
using System;
using System.Threading;
public class Worker
{
// This method will be called when the thread is started.
public void DoWork()
{
while (!_shouldStop)
{
Console.WriteLine("worker thread: working...");
}
Console.WriteLine("worker thread: terminating gracefully.");
}
public void RequestStop()
{
_shouldStop = true;
}
// Volatile is used as hint to the compiler that this data
// member will be accessed by multiple threads.
private volatile bool _shouldStop;
}
public class WorkerThreadExample
{
static void Main()
{
// Create the thread object. This does not start the thread.
Worker workerObject = new Worker();
Thread workerThread = new Thread(workerObject.DoWork);
// Start the worker thread.
workerThread.Start();
Console.WriteLine("main thread: Starting worker thread...");
// Loop until worker thread activates.
while (!workerThread.IsAlive);
// Put the main thread to sleep for 1 millisecond to
// allow the worker thread to do some work:
Thread.Sleep(1);
// Request that the worker thread stop itself:
workerObject.RequestStop();
// Use the Join method to block the current thread
// until the object's thread terminates.
workerThread.Join();
Console.WriteLine("main thread: Worker thread has terminated.");
}
}
샘플 출력
main thread: starting worker thread... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: working... worker thread: terminating gracefully... main thread: worker thread has terminated
참고 항목
작업
참조
스레딩(C# 프로그래밍 가이드)
스레딩 사용(C# 프로그래밍 가이드)
volatile(C# 참조)
Thread
Mutex
Monitor
Start
IsAlive
Sleep
Join
Abort