다음을 통해 공유


방법: 스레드 만들기 및 종료(C# 프로그래밍 가이드)

업데이트: 2007년 11월

이 예제에서는 보조 또는 작업자 스레드를 만들고 기본 스레드와 함께 이 스레드를 병렬로 사용하여 작업 처리를 수행하는 방법을 보여 줍니다. 또한 다른 스레드의 작업이 끝날 때까지 한 스레드가 대기하도록 만들고 스레드를 올바르게 종료하는 방법도 보여 줍니다. 다중 스레딩에 대한 배경 정보는 관리되는 스레딩스레딩 사용(C# 프로그래밍 가이드)을 참조하십시오.

이 예제에서는 작업자 스레드에서 실행할 DoWork 메서드가 포함된 Worker라는 클래스를 만듭니다. 이는 본질적으로 작업자 스레드의 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# 프로그래밍 가이드)

스레딩 사용(C# 프로그래밍 가이드)

Thread

volatile(C# 참조)

Mutex

Monitor

Start

IsAlive

Sleep

Join

Abort

기타 리소스

관리되는 스레딩

스레드 샘플