標籤:機率 代碼 線程 問號 tgt 主線程 運行 上下文 支援
第1部分:起步
介紹和概念
c#支援通過多線程並存執行代碼。線程是一個獨立的執行路徑,能夠與其他線程同時運行。
一個c#用戶端程式(控制台、WPF或Windows表單)從一個由CLR和作業系統(“主”線程)自動建立的線程開始,並且通過建立額外的線程來實現多線程。下面是一個簡單的樣本及其輸出:
所有的樣本都假設匯入了以下名稱空間:
使用系統;
使用System.Threading;
類ThreadTest
{
靜態void Main()
{
線程t = new Thread(WriteY);/ /啟動一個新線程
t.Start();/ /運行WriteY()
/ /同時,在主線程上做一些事情。
for(int i = 0;i < 1000;i + +)控制台。寫(“x”);
}
靜態孔隙WriteY()
{
for(int i = 0;i < 1000;i + +)控制台。寫(“y”);
}
}
xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyy
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
yyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
……
主線程建立一個新的線程t,它運行一個重複列印字元“y”的方法。同時,主線程反覆列印字元“x”:
開始一個新的線程
一旦啟動,線程的IsAlive屬性將返回true,直到線程結束。當傳遞到線程的建構函式的委託完成執行時,線程結束。一旦結束,線程無法重新啟動。
CLR將每個線程分配其自己的記憶體堆棧,這樣本地變數就會保持分離。在下一個樣本中,我們定義了一個帶有局部變數的方法,然後在主線程和新建立的線程上同時調用方法:
靜態void Main()
{
新線程(Go). start();/ /調用Go()在新線程上
Go();/ / Call Go()在主線程上
}
靜態空去()
{
/ /聲明並使用局部變數—“迴圈”
for(int cycle = 0;迴圈< 5;迴圈+ +)控制台。寫(‘ ? ‘);
}
? ? ? ? ? ? ? ? ? ?
每個線程的記憶體堆棧上都建立了一個單獨的迴圈變數副本,因此可以預見,輸出是10個問號。
如果線程共用同一個對象執行個體的公用引用,則共用資料。例如:
類ThreadTest
{
bool完成;
靜態void Main()
{
ThreadTest tt = new ThreadTest();/ /建立一個通用執行個體
新線程(tt.Go).Start();
tt.Go();
}
/ /注意,Go現在是一個執行個體方法
空去()
{
如果(完成){ done = true;控制台。WriteLine(“完成”);}
}
}
因為兩個線程都在同一個ThreadTest執行個體上調用Go(),它們共用完成的欄位。這樣做的結果是“完成”,而不是兩次。
完成
靜態欄位提供了線上程之間共用資料的另一種方法。這裡有一個與靜態欄位相同的例子:
類ThreadTest
{
靜態bool完成;/ /靜態欄位在所有線程之間共用
靜態void Main()
{
新線程(去).Start();
();
}
靜態空去()
{
如果(完成){ done = true;控制台。WriteLine(“完成”);}
}
}
這兩個例子都說明了另一個關鍵概念:安全執行緒(或者更確切地說,是安全執行緒)。輸出實際上是不確定的:有可能(儘管不太可能)“完成”可以列印兩次。但是,如果我們在Go方法中交換語句的順序,“完成”的機率就會急劇增加:
靜態空去()
{
如果(!){控制台。WriteLine(“Done”);Done = true;
}
完成
完成(通常是!)
問題是,一個線程可以對if語句進行評估,而另一個線程正在執行WriteLine語句——在它有機會設定為true之前。
補救辦法是在閱讀和寫作時獲得一個獨佔鎖。c#為這個目的提供了鎖語句:
類為
{
靜態彎曲件;
靜態readonly對象locker = new object();
靜態void Main()
{
新線程(去).Start();
();
}
靜態空去()
{
鎖(鎖)
{
如果(!){控制台。WriteLine(“Done”);Done = true;
}
}
}
當兩個線程同時爭用一個鎖(在本例中是一個鎖),一個線程等待,或者阻塞,直到鎖可用。在這種情況下,它只確保一個線程可以一次輸入關鍵的程式碼片段,並且“完成”將只列印一次。以這種方式保護的代碼—從多線程上下文中的不確定性—稱為安全執行緒。
共用資料是多線程中複雜性和模糊錯誤的主要原因。雖然通常是必要的,但要儘可能的簡單。
線程雖然被阻塞,但不會消耗CPU資源。
加入和睡眠
您可以通過調用它的聯結方法等待另一個線程結束。例如:
靜態void Main()
{
線程t =新線程
C# 的線程