C#中的線程池使用(二)


線程池是后台線程。每個線程都使用默認的堆棧大小,以默認的優先級運行,並處於多線程單元中。每個進程只有一個線程池對象。

下面說一下線程池中的異常,在線程池中未處理的異常將終止進程。以下為此規則的三種例外情況:

(1)由於調用了Abort,線程池線程中將引發ThreadAbortException異常(在對Abort方法進行調用時引發的異常)。

(2)由於正在卸載應用程序域,線程池線程中將引發AppDomainUnloadedException異常(在嘗試訪問已卸載的應用程序域時引發的異常)。

(3)公共語言運行庫或宿主進程將終止線程。

如果公共語言運行庫所創建的線程中未處理這些異常中的任何一個,則異常將終止線程,但公共語言運行庫不允許該異常繼續下去。

如果在主線程或從非托管代碼進入運行庫的線程中未處理這些異常,則它們將正常繼續,並導致應用程序終止。

注意在 .NET Framework 1.0 和 1.1 版中,公共語言運行庫將捕獲線程池中的未處理異常,而不出現任何提示。這可能會破壞應用程序狀態,並最終導致應用程序掛起,將很難進行調試。

使用線程池的方式主要有4種,下面分別對其進行介紹。

1.ThreadPool類的QueueUserWorkItem方法

在使用線程池時,可以從托管代碼中調用ThreadPool類的QueueUserWorkItem方法,或從非托管代碼中調用CorQueueUserWorkItem方法,並用線程池線程要執行的回調方法WaitCallback執行線程池。

QueueUserWorkItem方法將方法排入隊列以便執行(並指定包含該方法所用數據的對象,用state參數來實現)。此方法在有線程池線程變得可用時執行。該方法有兩個語法形式,其語法如下:

public static bool QueueUserWorkItem(WaitCallback callBack)
public static bool QueueUserWorkItem(WaitCallback callBack,Object state)

參數說明:

callBack:WaitCallback類型,它表示要執行的方法。

State:Object類型,包含方法所用數據的對象。

返回值:Boolean類型,如果此方法成功排隊,則為true;如果無法將該工作項排隊,則引發OutOfMemoryException異常。 

  示例   線程池的應用

本示例用線程池順序執行兩個方法。代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ThreadPoolApple
{
    class Program
    {
        public void thread1(Object obj)//定義方法thread1
        {
            for (int i = 0; i <= 3; i++)//輸出0~3的值
            {
                Console.Write(i.ToString());
            }
            Console.WriteLine();//換行
        }
        public void thread2(Object obj)//定義方法thread2
        {
            for (int i = 4; i <= 6; i++)//輸出4~6的值
            {
                Console.Write(i.ToString() + obj.ToString());//值后面加貨幣符號
            }
            Console.WriteLine();//換行
        }
        static void Main(string[] args)
        {
            string Ostr = "";//用字符串記錄貨幣符號
            Program prog = new Program();//實例化Program類
            for (int i = 0; i <= 3; i++)
            {
                //用線程池執行無參數方法
                ThreadPool.QueueUserWorkItem(new WaitCallback(prog.thread1));
                //用線程池執行有參數方法
                ThreadPool.QueueUserWorkItem(new WaitCallback(prog.thread2),Ostr);
            }
            Console.ReadLine();
        }
    }
}

運行結果如下圖所示。

 

圖  線程池的應用結果

 注意:在ThreadPool類中QueueUserWorkItem是一個靜態方法,因此可以由ThreadPool類直接用。

2.ThreadPool類的UnsafeQueueUserWorkItem方法

該方法注冊一個等待WaitHandle的委托。與QueueUserWorkItem方法不同,UnsafeQueueUserWorkItem 不會將調用堆棧傳播到輔助線程。這使得代碼可以失去調用堆棧,從而提升它的安全特權。語法如下:

[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.NoFlags|SecurityPermissionFlag.ControlEvidence|SecurityPermissionFlag.ControlPolicy)]
public static bool UnsafeQueueUserWorkItem(WaitCallback callBack,Object state)

參數說明:

callBack :System.Threading.WaitCallback類型,一個WaitCallback,表示當線程池中的線程選擇工作項時調用的委托。

state:Object類型,在接受線程池服務時傳遞給委托的對象。

返回值:Boolean類型,如果方法成功,則為true;如果無法將該工作項排隊,則引發OutOfMemoryException。

在使用 UnsafeQueueUserWorkItem方法時,可能會無意中打開一個安全漏洞。代碼訪問安全性的權限檢查基於所有調用方對堆棧的權限進行。如果使用 UnsafeQueueUserWorkItem 將工作排在某個線程池線程上,則該線程池線程的堆棧將不會具有實際調用方的上下文。惡意代碼可能會利用這一點避開權限檢查。

  示例     線程池的應用

本示例主要講解一下如何用UnsafeQueueUserWorkItem方法對線程池進行操作。代碼如下:

namespace _01_05
{
    class Program
    {
        static void Main(string[] args)
        {
            //注冊一個等待WaitHandle的委托
            ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(ThreadProc), "First task");
            Console.WriteLine("將主線程掛起");
            Thread.Sleep(1000);//掛走主線程
            Console.WriteLine("執行主線程");
            Console.ReadLine();
        }
        //自定義方法,主要用於線程池的調用方法
        public static void ThreadProc(object state)
        {
            Console.WriteLine("執行線程池   "+state.ToString());
        }
    }
}

運行結果如下圖所示。

 

與QueueUserWorkItem方法不同,UnsafeQueueUserWorkItem不會將調用堆棧傳播到輔助線程。這使得代碼可以失去調用堆棧,從而提升它的安全特權。

3.ThreadPool類的RegisterWaitForSingleObject方法

可以使用 ThreadPool類的RegisterWaitForSingleObject方法注冊正在等待WaitHandle的委托。該方法一共有4個語法形式,下面分別對其進行介紹。

l  語法1

注冊一個等待WaitHandle的委托,並指定一個32位有符號整數來表示超時值(以毫秒為單位)。其語法如下:

public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject,WaitOrTimerCallback callBack,Object state,int millisecondsTimeOutInterval,bool executeOnlyOnce)

waitObject:WaitHandle類型,要注冊的WaitHandle。使用WaitHandle而非Mutex。

CallBack:WaitOrTimerCallback類型,waitObject參數終止時調用的WaitOrTimerCallback委托。

State:Object類型,傳遞給委托的對象。

MillisecondsTimeOutInterval:Int32類型,以毫秒為單位的超時。如果millisecondsTimeOutInterval參數為0(零),函數將測試對象的狀態並立即返回。如果millisecondsTimeOutInterval為-1,則函數的超時間隔永遠不過期。

ExecuteOnlyOnce:Boolean類型,如果為true,表示在調用了委托后,線程將不再在waitObject 參數上等待;如果為false,表示每次完成等待操作后都重置計時器,直到注銷等待。

返回值:System.Threading.RegisteredWaitHandle類型,封裝本機句柄的 RegisteredWaitHandle。

l  語法2

注冊一個等待WaitHandle的委托,並指定一個64位有符號整數來表示超時值(以毫秒為單位)。其語法如下:

public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject,WaitOrTimerCallback callBack,Object state,long millisecondsTimeOutInterval,bool executeOnlyOnce)

參數說明:

waitObject:WaitHandle類型,要注冊的WaitHandle。使用WaitHandle而非Mutex。

CallBack:WaitOrTimerCallback類型,waitObject參數終止時調用的WaitOrTimerCallback委托。

State:Object類型,傳遞給委托的對象。

millisecondsTimeOutInterval:Int64類型,以毫秒為單位的超時。如果millisecondsTimeOutInterval參數為0(零),函數將測試對象的狀態並立即返回。如果millisecondsTimeOutInterval為-1,則函數的超時間隔永遠不過期。

ExecuteOnlyOnce:Boolean類型,如果為true,表示在調用了委托后,線程將不再在waitObject 參數上等待;如果為false,表示每次完成等待操作后都重置計時器,直到注銷等待。

返回值:System.Threading.RegisteredWaitHandle類型,封裝本機句柄的 RegisteredWaitHandle。

l  語法3

注冊一個等待WaitHandle的委托,並指定一個TimeSpan值來表示超時時間。其語法如下:

public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject,WaitOrTimerCallback callBack,Object state,TimeSpan timeout,bool xecuteOnlyOnce)

參數說明:

waitObject:WaitHandle類型,要注冊的WaitHandle。使用WaitHandle而非Mutex。

CallBack:WaitOrTimerCallback類型,waitObject參數終止時調用的WaitOrTimerCallback委托。

State:Object類型,傳遞給委托的對象。

Timeout:TimeSpan類型,TimeSpan表示的超時時間。如果timeout為0(零),則函數將測試對象的狀態並立即返回。如果timeout為-1,則函數的超時間隔永遠不過期。

ExecuteOnlyOnce:Boolean類型,如果為true,表示在調用了委托后,線程將不再在waitObject 參數上等待;如果為false,表示每次完成等待操作后都重置計時器,直到注銷等待。

返回值:System.Threading.RegisteredWaitHandle類型,封裝本機句柄的 RegisteredWaitHandle。

l  語法4

指定表示超時(以毫秒為單位)的32位無符號整數,注冊一個委托等待WaitHandle。其語法如下:

[CLSCompliantAttribute(false)]
public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject,WaitOrTimerCallback callBack,Object state,uint millisecondsTimeOutInterval,bool executeOnlyOnce)

參數說明:

waitObject:WaitHandle類型,要注冊的WaitHandle。使用WaitHandle而非Mutex。

CallBack:WaitOrTimerCallback類型,waitObject參數終止時調用的WaitOrTimerCallback委托。

State:Object類型,傳遞給委托的對象。

MillisecondsTimeOutInterval:UInt32類型,以毫秒為單位的超時。如果millisecondsTimeOutInterval參數為0(零),函數將測試對象的狀態並立即返回。如果millisecondsTimeOutInterval為-1,則函數的超時間隔永遠不過期。

ExecuteOnlyOnce:Boolean類型,如果為true,表示在調用了委托后,線程將不再在waitObject參數上等待;如果為false,表示每次完成等待操作后都重置計時器,直到注銷等待。

返回值:System.Threading.RegisteredWaitHandle類型,封裝本機句柄的 RegisteredWaitHandle。

注意:在以上的4個語法中,對waitObject應用Mutex不會導致回調互斥,因為它是基於Win32 API使用默認的WT_EXECUTEDEFAULT標志,所以每次回調都在單獨的線程池線程上調度。因此,請盡可能的不要使用Mutex,應該使用最大計數為1的Semaphore。

RegisterWaitForSingleObject方法將指定的委托排隊到線程池。當發生以下兩種情況時,輔助線程將執行委托:

l  指定對象處於終止狀態。

l  超時間隔已過期。

RegisterWaitForSingleObject方法檢查指定對象的WaitHandle的當前狀態。如果對象狀態為非終止狀態,則此方法將注冊一個等待操作,該等待操作由線程池中的一個線程來執行。當對象狀態變為終止或超時間隔已過期時,委托由輔助線程執行。如果 timeOutInterval參數不為0(零),並且executeOnlyOnce參數為false,則每當事件收到信號或超時間隔過期時都會重置計時器。若要取消等待操作,請調用RegisteredWaitHandle.Unregister方法。

等待線程使用Win32的WaitForMultipleObjects函數來監視已注冊的等待操作。因此,如果必須在對RegisterWaitForSingleObject的多次調用中使用相同的本機操作系統句柄,則必須使用Win32的DuplicateHandle函數重復該句柄。這里要注意的是,不應為傳遞到RegisterWaitForSingleObject的事件對象發出脈沖,這是因為等待線程在重置前可能不會檢測到該事件已終止。

返回前,函數將修改某些類型的同步對象的狀態。修改僅發生在其終止狀態滿足等待條件的對象上。例如,信號量計數減少一。

下面通過一個簡單的例子,來說明一下如何使用RegisterWaitForSingleObject方法對線程池進行相應的操作。代碼如下:

  示例    線程池的應用

本示例通過注冊正在等待的WaitHandle委托,對線程池進行操作。代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace _01_04
{
    class Program
    {
        public class TaskInfo//定義一個類
        {
            public RegisteredWaitHandle Handle = null;//定義一個句柄變量
            public string OtherInfo = "default";//定義一個字符串
        }
        static void Main(string[] args)
        {
            AutoResetEvent ev = new AutoResetEvent(false);//實例化等待線程已發生的事件,將初始狀態設置為非終止
            TaskInfo ti = new TaskInfo();//實例化類
            ti.OtherInfo = "First task";//記錄文本信息
            ti.Handle = ThreadPool.RegisterWaitForSingleObject(
                ev,//要進行注冊的WaitHandle類型
                new WaitOrTimerCallback(WaitProc),//當WaitHandle超時或終止時要調用的方法
                ti,//傳遞給委托對象的值
                1000,//設置超時間隔
                false//表示每次完成等待操作后都重置計時器,直到注銷等待
            );//注冊一個等待的委托
            Thread.Sleep(3100);//將主線程掛起
            Console.WriteLine("執行主線程");
            ev.Set();//將WaitHandle設置為終止
            Console.WriteLine("將等待線程已發生的事件設置為終止");
            Console.ReadLine();
        }
        //state:一個對象,包含回調方法在每次執行時要使用的信息
        //timedOut:如果 WaitHandle 超時,則為 true;如果其終止,則為 false。
        public static void WaitProc(object state, bool timedOut)
        {
            TaskInfo ti = (TaskInfo)state;
            if (!timedOut)//當WaitHandle為終止時
            {
                if (ti.Handle != null)//如果委托對象的句柄不為空
                    ti.Handle.Unregister(null);//取消RegisterWaitForSingleObject方法所發出的已注冊等待操作
            }
            string StrTime = timedOut ? " WaitHandle 超時" : "終止";
            Console.WriteLine("當前執行所使用的信息:" + state.ToString() + "" + StrTime + ";  傳遞給委托的對象:" + ti.OtherInfo);

        }
    }
}

運行結果如下圖所示。

 

圖  RegisterWaitForSingleObject方法的應用

在上圖可以看出,在上例並沒有使用for等循環語句執行線程池(ThreadPool)中的RegisterWaitForSingleObject方法,但它卻執行了3次,這是為什么呢?其主要原因是將RegisterWaitForSingleObject方法中的ExecuteOnlyOnce參數設置為false(如果將其設置為true,調用了委托后,線程將不在waitObject參數上等待,也就是只執行一次),它表示每次完成等待操作后都重置計時器,直到取消由RegisterWaitForSingleObject方法發出的已經注冊等待的操作,主要是用State參數來實現的,為了可以在該參數中記錄所傳遞的委托對象,事先必須要定義一個類,在類中定義兩個全局變量,用於記錄委托的信息,以及本機的句柄。其類的定義如下:

public class TaskInfo//定義一個類
{
    public RegisteredWaitHandle Handle = null;//定義一個句柄變量
    public string OtherInfo = "default";//定義一個字符串
}

然后通過自定義類的Handle變量記錄本機句柄,用OtherInfo變量記錄委托信息,如果想要取消RegisterWaitForSingleObject方法所發出的已注冊的等待操作,可以用Handle變量的Unregister(null)方法取消注冊。

為了讀者能更好的定義RegisterWaitForSingleObject方法所執行的自定義事件,下面對RegisterWaitForSingleObject方法中CallBack參數所調用的方法進行說明,CallBack參數是WaitHandle類型的,主要是當WaitHandle超時或終止時所調用的方法,其語法結構為:

[ComVisibleAttribute(true)]
public delegate void WaitOrTimerCallback(Object state,bool timedOut)

參數說明:

state:Object類型,一個對象,包含回調方法在每次執行時要使用的信息。

TimedOut:Boolean類型,如果WaitHandle超時,則為true;如果其終止,則為false。

通過以上語法,用戶可自定義一個方法,用於線程池的調用,代碼如下:

public static void WaitProc(object state, bool timedOut)
{

}


免責聲明!

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



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