如何:創建和終止線程(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 方法只是將 true 賦給 _shouldStop 數據成員。由於此數據成員由 DoWork 方法來檢查,因此這會間接導致 DoWork 返回,從而終止輔助線程。但是,需要注意:DoWork 和 RequestStop 將由不同線程執行。DoWork 由輔助線程執行,而 RequestStop 由主線程執行,因此 _shouldStop 數據成員聲明爲 volatile,如下所示:

private volatile bool _shouldStop;

volatile 關鍵字用於通知編譯器,將有多個線程訪問 _shouldStop 數據成員,因此它不應當對此成員的狀態做任何優化假設。有關更多信息,請參見 volatile(C# 參考)

通過將 volatile 與 _shouldStop 數據成員一起使用,可以從多個線程安全地訪問此成員,而不需要使用正式的線程同步技術,但這僅僅是因爲 _shouldStop 是 bool。這意味着只需要執行單個原子操作就能修改 _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 來將主線程中斷片刻。這保證了輔助線程的 DoWork 函數在 Main 函數執行其他任何命令之前,在 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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章