C#學習筆記-多線程
羅朝輝(http://www.cnblogs.com/kesalin/)
《C#與.NET進階程式設計》讀書筆記
1,在.NET平台下,應用程式定義域和線程之間不是一一對應的,一個應用程式定義域可以有多個線程,而一個特定的線程在它的生命週期內不一定被限定在一個應用程式定義域中。Win32線程發送器和CLR會根據需要讓線程能夠自由地跨越應用程式定義域的邊界,但任何特定時刻,一個線程只能運行在一個應用程式定義域中。System.Threading命名空間定義了與線程相關的類型。當希望訪問(正在承載當前線程的)應用程式定義域時,請使用Thread.GetDomain()。在任何特定時刻,一個線程也可以移動到一個特定的上下文,並有CLR重新部署在一個新的上下文中。使用Thread.CurrentContext屬性可以獲得正在執行中的線程的當前上下文。CLR控制線程移入/移出應用程式定義域或上下文。
2,可以使用非同步委託來自動建立次線程以外處理非同步方法呼叫調用。System.Threading命名空間也定義了很多用來同步訪問共用資源的類型。
3,前台線程能阻止應用程式的終結。一直到所有的前台線程終止後,CLR才能關閉應用程式(即卸載承載的應用程式定義域)。後台線程(有時也叫守護線程)被CLR認為是程式執行中可做出犧牲的途徑,即在任何時候(即使這個線程此時正在執行某項工作)都可能被忽略。因此,如果所有的前台線程終止,當應用程式定義域卸載時,所以的後台線程也會被自動終止。
4,同步訪問共用資源的首選技術是C#的 lock 關鍵字,lock 允許定義一段線程同步的代碼語句,它需要定義一個標記(即一個對象引用),線程在進入鎖定範圍的時候必須獲得這個標記,在退出鎖定範圍時需要釋放鎖。當試圖鎖定的是一個執行個體級的私人方法時,使用方法本身所在對象的引用就可以了。然而,如需鎖定公用成員中的一段代碼,比較安全的做法是聲明私人的object成員作為鎖標識。如:
public class Printer
{
// 鎖標識
private object threadLock = new object();
public void PrintNumbers()
{
// 使用鎖標識
lock (threadLock)
{
...
}
}
若試圖鎖定靜態方法中的代碼,只需要聲明一個私人靜態對象成員作為鎖定 Token就可以了。
5,C# lock 聲明實際上是和 System.Threading.Moniter類一同使用時的速記符號。經過編譯器的處理,鎖定地區實際上被轉化成了如下內容(可使用 ildasm.exe查看):
public void PrintNumbers()
{
Monitor.Enter(threadLock);
try
{
...
}
finally
{
Monitor.Exit(threadLock);
}
}
6,System.Threading.Interlocked 允許我們對資料進行一些原子操作:CompareExchange(), Decrement(), Exchange(), Increment()。這些靜態方法需要以引用方式傳入變數。如:注意newVal 和 intVal 的值都是遞增之後的值。
public void AddOne()
{
int newVal = Interlocked.Increment(ref intVal);
}
7,[Synchronization]特性可以有效地使對象的所以執行個體的成員都保持安全執行緒。當CLR分配帶[Synchronization]特性的對象時,它會把這個對象放在同步上下文中。這是編寫安全執行緒代碼的一種“偷懶”方式,因為它不需要我們實際深入線程式控制制敏感性資料的細節,但這種方式對效能有影像,因為即使一個方法沒有使用共用資源,CLR仍然會鎖定對該方法的調用。
8,System.Threading命名空間中定義了 ThreadPool,線程池是由CLR維護的。System.Threading命名空間中還有 Timer 類型和與其相關的 TimerCallback 委託。
9,BackgroundWorker 組件:當構建一個圖形化的Windows Form傳統型應用程式並且需要執行在應用程式UI線程之外的線程中長時間的任務(如調用遠程Web服務,進行資料庫事務等)時,BackgroundWorker類就很有用。該類的編程模型利用了很多結合非同步委託的相同線程文法。我們只需要告訴BackgroundWorker我們希望在後台執行哪個方法並調用RunWorkerAsync()即可。調用線程繼續運行,而工作方法會被非同步執行,當工作方法執行完畢,BackgroundWorker就會通過觸發 RunWorkerCompleted 事件來通知調用線程。