非同步操作(一)

iDotNetSpace發表於2010-05-07
筆者想說說這裡為什麼談非同步操作。其實這裡跟筆者大學學到的系統結構相似,筆者還記得老師要筆者經常做的習題,就是許多外部裝置並行工作,具體流程是這樣的:裝置1向CPU發出資料傳送請求,CPU處理請求,下達命令,併發生中斷,裝置1通過通道或DMA方式進行管理資料的傳送,然後是裝置2向CPU發出資料請求,CPU處理請求,下達命令,......裝置1向CPU發出資料傳送完畢請求,CPU作出相應的處理(主要是完成資料傳送完畢的收尾處理,還原到斷點等等相關操作),也有可能是裝置2資料傳送完畢向CPU發出資料傳送完畢請求,CPU作出相應的處理。這裡體現了一個非同步操作。如果這裡用到的是同步操作的話,過程是這樣的裝置1向CPU發出資料傳送請求,CPU處理請求,並等待資料傳送完畢(CPU空閒),資料傳送完畢CPU作出相應的處理。裝置2.....。這裡相信大家能明白一些用非同步操作的好處了。所以我們在.NET中程式設計也應該考慮下這方面的情況,適當用些非同步程式設計,提高程式的效率。

  執行緒概述

  執行緒是程式最終執行者,它不擁有資源,但是有自己的堆疊和一些暫存器。多執行緒的提供增強了系統健壯性。如果某個程式的執行緒由於某種原因進入了無限迴圈或者被掛起後,這時一個執行緒被凍結,而其他程式的執行緒和系統本身還可以繼續執行。

       其實執行緒的系統開銷很大。建立一個執行緒的開銷:系統必須為執行緒分配並初始化一個執行緒核心物件,還必須為每個執行緒保留1Mb的地址空間用於執行緒的使用者模式堆疊,分配12KB的地址空間用於執行緒的核心模式堆疊。Windows呼叫程式中每個dll都有一個函式來通知程式中所有的dll作業系統建立一個新的執行緒。同樣銷燬一個執行緒的開銷也不小:程式中的每個dll都要接受一個關於該執行緒即將“死亡”的通知,並且堆疊要釋放。相信大家學過作業系統。多個執行緒如何執行這裡就不在介紹,但是必須說明一點多個執行緒執行,對系統的開銷很大。

       總之,應該儘可能限制執行緒的使用。如果建立的執行緒越多,給作業系統帶來的開銷就越大,而且所有應用程式執行得越慢。也會佔用更多的記憶體。

 

  CLR執行緒池簡介

      CLR中包含管理執行緒池(thread pool)的程式碼。它就是為了改進上述的現象。我們可以將執行緒池看做應用程式自己使用的執行緒集合。每個程式都有一個執行緒池,這個執行緒池被該程式中的所有應用程式域共享。

       當CLR初始化時,執行緒池中還沒有任何執行緒。從內部實現上講,執行緒池維護了一系列操作請求。應用程式希望執行一個非同步操作時,可以呼叫一些方法線上程池的佇列中加入一個條目。執行緒池中的程式碼將從這個佇列中提取出條目,並將該條目分配到執行緒池中的執行緒。如果執行緒中沒有任何執行緒,就建立一個新執行緒。建立一個執行緒會有相關效能損失。但是,當執行緒池中的執行緒完成任務時,並不會被銷燬,而是返回到執行緒池中,線上程池中空閒,等待另外的請求。因為執行緒不對它自身進行銷燬,所以此處不會帶來效能損失。

       如果應用程式對執行緒池進行了很多的請求,那麼執行緒池將試圖用一個執行緒來響應所有的請求。但是如果應用程式排隊的請求超出了執行緒池的處理能力,執行緒池中將建立另外的執行緒,最終應用程式排隊的請求與執行緒池中的執行緒的處理能力達到一個平衡點,我們用較少的執行緒來處理所有請求。

       如果應用程式停止請求執行緒池,如果執行緒池中的執行緒空閒時間超過大約2分鐘,執行緒將會喚醒自己,並終止自己,以釋放記憶體資源,並會存在一個效能損失。但是不會太嚴重,因為執行緒終止自己的時候,執行緒應經處於空閒狀態,也就是說應用程式當前沒有執行太多的事情。

       因此我們可以看出執行緒池它管理建立較少的執行緒以避免浪費資源與建立較多的執行緒以利用多處理器,超執行緒處理器和多核處理器之間的平衡。執行緒池也是啟發式地,如果應用程式需要執行許多工,而且CPU的數量足夠使用,那麼執行緒池將建立更多的執行緒。如果應用程式的工作負載降下來,那麼執行緒池中的執行緒將終止自己。

       執行緒池將執行緒池中的執行緒進行分類,劃分為工作程式(worker thread)和I/O執行緒(I/O thread).當應用程式請求執行緒池執行一個受計算限制的非同步操作時使用工作執行緒。而I/O執行緒用於在受I/O限制的非同步操作完成時通知程式碼。

       CLR的執行緒池允許開發人員設定工作執行緒和I/O執行緒的最大數量。CRL保證建立的執行緒數量不會超過這個設定值。在CLR2.0中將工作執行緒默然最大數量設定為25,I/O執行緒最大限制為1000個。

 

  執行非同步操作

      為將一個受計算限制的非同步操作加入到執行緒池的佇列中,一般可以呼叫ThreadPool類中定義的方法:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gtstatic Boolean QueueUserWorkItem(WaitCallback callBack);
static Boolean QueueUserWorkItem(WaitCallback callback,Object state);
static Boolean UnsafeQueueUserWorkItem(WaitCallback callback,Ojbect State)

上述方法將一個“工作項”加入到執行緒池的佇列中,然後這些方法就會立即返回。工作項是一個由CallBack引數標識的方法,執行緒池的執行緒將呼叫該方法。該方法可以只傳遞一個單獨的由state引數指定的引數。沒有state引數的QuereUserWrokItem方法為回撥函式傳遞null。最終執行緒池中的一些執行緒將執行工作項,從而導致我們的方法被呼叫。我們這裡寫的回撥方法必須匹配System.Threadding.WaitCallBack委託型別,定義如下:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gtdelegate void WaitCallBack(Ojbect state);

 同時給出一個非同步呼叫例子:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gtusing System;
using System.Threading;

public static class Progrom
{
public static void Main()
{
Console.WriteLine(“Main thread:queuing an asynchronous operation”);
//委託採用了簡寫
ThreadPool.QueueUserWorkItem(CoputeBoundOp,5);
Console.WriteLine(“Main thread:Doing other work here”);
//模擬其他工作
Thread.Sleep(10000);
}

//該方法由執行緒池中的執行緒執行
private static void ComputeBoundOp(Object state)
{
Console.Write(“In ComputeBoundOp:State
={0}”,state);
Thread.Sleep(
1000);

//這個方法返回後,執行緒就回到執行緒池中,然後等待執行另一個任務
}
}

UnsafeQueueUserWorkItem方法與QueueUserWorkItem非常相似。它們區別:試圖訪問一個受限的資源(例如,開啟一個檔案)時,CLR將執行一個程式碼訪問安全檢查。即,CLR將檢查呼叫的執行緒的呼叫堆疊中的所有程式集時候都有訪問資源的許可許可權。如果有一些程式集沒有所需的許可許可權,CLR將丟擲一個SecurityExcepation異常。假設正在執行程式碼的執行緒所在的程式集沒有開啟檔案的許可權,那麼線上程試圖開啟檔案時,CLR將丟擲一個SecurityException異常。為了讓執行緒繼續執行,執行緒可以線上程池的佇列中加入一個工作項,讓執行緒池的執行緒來執行開啟檔案的程式碼。當然這個必須在擁有合適許可許可權的程式集中進行。這種“工作區”智取安全許可權的現象可以允許惡意的程式碼對受限資源進行嚴重破壞。為了阻止這中獲得安全許可權的方式,QueueUserWorkItem方法內部遍歷呼叫執行緒堆疊,並捕獲所有被授予的安全許可權。然後,當執行緒池中的執行緒開始執行時,這些許可權在於執行緒結合。因此,執行緒池中的執行緒以呼叫QueueUserWorkItem方法的執行緒相同的許可權集來完成執行。遍歷執行緒堆疊並捕獲所有安全許可權與效能緊密相關。如果希望改進受計算限制的非同步操作的排隊效能,可以呼叫UnsafeQueueUserWorkItem方法,該方法只將工作項加入執行緒池的佇列中,而不是遍歷所有的執行緒堆疊。但是僅當確認執行緒池中的執行緒執行的程式碼不觸及受限的資源時,或者確信接觸這部分資源不會出現問題時,才呼叫該方法。呼叫UnsafeQueueUserWorkItem方法的程式碼需要使SecuriityPermission的ControlPolicy標記和ControlEvidence標記開啟,可以阻止未信任的程式碼偶然或者故意提升它的許可許可權。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-662143/,如需轉載,請註明出處,否則將追究法律責任。

相關文章