C# 線程手冊 第二章 .NET 中的線程 線程的生命周期


當一個線程計划執行時它可以經過幾個狀態,包括未開始,活躍,睡眠,等等。線程類包含幾個允許你啟動、停止、恢復、退出、暫停以及等待一個線程的方法。我們可以使用ThreadState屬性來獲取線程的當前狀態,狀態值可能是ThreadState枚舉中的一個:

Aborted - 線程當前處理停止狀態,但是不一定已經執行完。

AbortRequested – 已經調用Abort() 方法但是線程還沒有接收到將試圖終止線程的System.Threading.ThreadAbortexception。雖然線程還沒有停止,但是馬上就會。

Background - 線程在后台執行。

Running - 線程已經啟動而且沒有被阻塞。

Stopped - 線程已經完成了所有指令並停止了。

StopRequested - 請求線程停止。

Suspend - 線程已經被掛起。

SuspendRequested - 請求掛起線程。

Unstarted - 線程還沒有調用Start()方法之前。

WaitSleepJoin - 調用Wait(), Sleep() 或 Join() 方法后處理阻塞狀態的線程。

圖3 顯示了一個線程的生命周期。

圖3

在這部分,我們將討論線程的生命周期

 

讓一個線程進入睡眠狀態

當我們創建一個線程后,我們需要調用線程對象的Start()方法來調度那個線程。在這時,CLR將會為作為構造函數參數傳遞給線程對象的方法地址分配一個時間片。一旦線程開始執行,它就可以在操作系統處理其他線程時回到睡眠狀態或者退出狀態。我們可以使用線程類的Sleep()方法讓一個線程進入睡眠狀態。如果你正在等待一個資源並且你想在稍后繼續嘗試訪問這個資源時,Sleep()方法是很重要的。舉個例子,假設你的程序由於無法訪問需要的資源而導致其不能繼續執行時,你可能想要在幾毫秒之后嘗試繼續訪問資源,在這種情況下讓線程在再次嘗試訪問資源之前睡眠一段時間是一個很好的方式。

Sleep()方法有兩種重載方式。第一種重載方法有一個整型參數,並會按照指定的毫秒時間暫停線程執行。例如,如果你向線程傳遞值100,那么線程將會暫停100毫秒。這個方法將會讓線程進入WaitSleepJoin狀態。讓我們看一個例子,thread_sleep2.cs:

/*************************************
/* Copyright (c) 2012 Daniel Dong
 * 
 * Author:Daniel Dong
 * Blog:  www.cnblogs.com/danielWise
 * Email: guofoo@163.com
 * 
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace SimpleThread
{
    public class ThreadSleep
    {
        public static Thread worker;
        public static Thread worker2;

        public static void Main()
        {
            Console.WriteLine("Entering the void Main!");

            worker = new Thread(new ThreadStart(Counter));
            worker2 = new Thread(new ThreadStart(Counter2));

            //Make the worker2 Object as highest priority
            worker2.Priority = ThreadPriority.Highest;

            worker.Start();
            worker2.Start();

            Console.WriteLine("Exiting the void Main!");
            Console.ReadLine();
        }

        public static void Counter()
        {
            Console.WriteLine("Entering Counter");
            for (int i = 1; i < 50; i++)
            {
                Console.Write(i + " ");
                if (i == 10)
                {
                    Console.WriteLine();
                    Thread.Sleep(1000);
                }
            }
            Console.WriteLine("Exiting Counter");
        }

        public static void Counter2()
        {
            Console.WriteLine("Entering Counter2");
            for (int i = 51; i < 100; i++)
            {
                Console.Write(i + " ");
                if (i == 70)
                {
                    Console.WriteLine();
                    Thread.Sleep(5000);
                }
            }
            Console.WriteLine("Exiting Counter2");
        }
    }
}

Counter()方法從1到50計數,當到達10以后睡眠1000毫秒。Counter2()方法從51~100計數,當到達70時睡眠5000毫秒。下面是輸出結果:

image

注:以上是在多核CPU下運行的結果,單核CPU 執行情況可能與上圖有所出入。

 

第二種重載方法有一個TimeSpan類型參數,當前線程會按照TimeSpan的值暫停一段時間。TimeSpan是System命名空間中的一個類。TimeSpan有一些很有用的屬性並會返回基於時鍾時間間隔。

我們可以使用FromSeconds()和FromMinutes()來確定睡眠時間。下面是一個例子,thread_sleep3.cs:

public static void Counter()
{
    Console.WriteLine("Entering Counter");
    for (int i = 1; i < 50; i++)
    {
        Console.Write(i + " ");
        if (i == 10)
        {
            Console.WriteLine();
            Thread.Sleep(TimeSpan.FromSeconds(1));
        }
    }
    Console.WriteLine("Exiting Counter");
}

public static void Counter2()
{
    Console.WriteLine("Entering Counter2");
    for (int i = 51; i < 100; i++)
    {
        Console.Write(i + " ");
        if (i == 70)
        {
            Console.WriteLine();
            Thread.Sleep(TimeSpan.FromSeconds(5));
        }
    }
    Console.WriteLine("Exiting Counter2");
}

輸出結果與thread_sleep2類似。

 

中斷一個線程

當讓一個線程睡眠時,它實際會進入WaitSleepJoin狀態。如果線程處理睡眠狀態,那么在它超時退出之前唯一可以喚醒線程的方式是使用Interrupt()方法。Interrupt()方法將讓線程回到調度隊列中去。讓我們看一個例子,thread_interrupt.cs:

/*************************************
/* Copyright (c) 2012 Daniel Dong
 * 
 * Author:Daniel Dong
 * Blog:  www.cnblogs.com/danielWise
 * Email: guofoo@163.com
 * 
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace SimpleThread
{
    public class Interrupt
    {
        public static Thread sleeper;
        public static Thread worker;

        public static void Main()
        {
            Console.WriteLine("Entering the void Main!");

            sleeper = new Thread(new ThreadStart(SleepingThread));
            worker = new Thread(new ThreadStart(AwakeThread));

            sleeper.Start();
            worker.Start();

            Console.WriteLine("Exiting the void Main!");
            Console.ReadLine();
        }
        public static void SleepingThread()
        {
            for (int i = 1; i < 50; i++)
            {
                Console.Write(i + " ");
                if (i == 10 || i == 20 || i == 30)
                {
                    Console.WriteLine("Going to sleep at: " + i);
                    try
                    {
                        Thread.Sleep(20);
                    }
                    catch (ThreadInterruptedException ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
            }
        }

        public static void AwakeThread()
        {
            for (int i = 51; i < 100; i++)
            {
                Console.Write(i + " ");
                if (sleeper.ThreadState == ThreadState.WaitSleepJoin)
                {
                    Console.WriteLine("Interrupting the sleeping thread.");
                    sleeper.Interrupt();
                }
            }
        }
    }
}

在上面的例子中,當計數器的值為10, 20 和 30 時第一個線程會睡眠。第二個線程會檢查第一個線程是否已經進入睡眠狀態。如果是的話,它將中斷第一個線程並使它回到調度隊列中去。Interrupt()方法是讓睡眠線程重新醒來的最好方式,當線程等待的資源可用且你想讓線程繼續運行時你可以使用這個方法。輸出結果與下面顯示的類似:

E(QX(]YAP9AJY~P[[(Z7V21

下一篇將會繼續介紹中斷和恢復線程…


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM