C 的多執行緒機制初探 3

2021-04-15 10:10:01 字數 3607 閱讀 7931

在這裡我們要注意的是其它執行緒都是依附於main()函式所在的執行緒的,main()函式是c#程式的入口,起始執行緒可以稱之為主線程,如果所有的前台執行緒都停止了,那麼主線程可以終止,而所有的後台執行緒都將無條件終止。而所有的執行緒雖然在微觀上是序列執行的,但是在巨集觀上你完全可以認為它們在並行執行。

讀者一定注意到了thread.threadstate這個屬性,這個屬性代表了執行緒執行時狀態,在不同的情況下有不同的值,於是我們有時候可以通過對該值的判斷來設計程式流程。threadstate在各種情況下的可能取值如下:

aborted:執行緒已停止

abortrequested:執行緒的thread.abort()方法已被呼叫,但是執行緒還未停止

background:執行緒在後台執行,與屬性thread.isbackground有關

running:執行緒正在正常執行

stopped:執行緒已經被停止

stoprequested:執行緒正在被要求停止

suspended:執行緒已經被掛起(此狀態下,可以通過呼叫resume()方法重新執行)

suspendrequested:執行緒正在要求被掛起,但是未來得及響應

unstarted:未呼叫thread.start()開始執行緒的執行

waitsleepjoin:執行緒因為呼叫了wait(),sleep()或join()等方法處於封鎖狀態

上面提到了background狀態表示該執行緒在後台執行,那麼後台執行的執行緒有什麼特別的地方呢?其實後台執行緒跟前臺執行緒只有乙個區別,那就是後台執行緒不妨礙程式的終止。一旦乙個程序所有的前台執行緒都終止後,clr(通用語言執行環境)將通過呼叫任意乙個存活中的後台程序的abort()方法來徹底終止程序。

當執行緒之間爭奪cpu時間時,cpu按照是執行緒的優先順序給予服務的。在c#應用程式中,使用者可以設定5個不同的優先順序,由高到低分別是highest,abovenormal,normal,belownormal,lowest,在建立執行緒時如果不指定優先順序,那麼系統預設為threadpriority.normal。給乙個執行緒指定優先順序

,我們可以使用如下**:

//設定優先順序為最低

mythread.priority=threadpriority.lowest;

通過設定執行緒的優先順序,我們可以安排一些相對重要的執行緒優先執行,例如對使用者的響應等等。

現在我們對怎樣建立和控制乙個執行緒已經有了乙個初步的了解,下面我們將深入研究執行緒實現中比較典型的的問題,並且**其解決方法。

三.執行緒的同步和通訊——生產者和消費者

假設這樣一種情況,兩個執行緒同時維護乙個佇列,如果乙個執行緒對佇列中新增元素,而另外乙個執行緒從佇列中取用元素,那麼我們稱新增元素的執行緒為生產者,稱取用元素的執行緒為消費者。生產者與消費者問題看起來很簡單,但是卻是多執行緒應用中乙個必須解決的問題,它涉及到執行緒之間的同步和通訊問題。

前面說過,每個執行緒都有自己的資源,但是**區是共享的,即每個執行緒都可以執行相同的函式。但是多執行緒環境下,可能帶來的問題就是幾個執行緒同時執行乙個函式,導致資料的混亂,產生不可預料的結果,因此我們必須避免這種情況的發生。c#提供了乙個關鍵字lock,它可以把一段**定義為互斥段(critical section),互斥段在乙個時刻內只允許乙個執行緒進入執行,而其他執行緒必須等待。在c#中,關鍵字lock定義如下:

lock(expression) statement_block

expression代表你希望跟蹤的物件,通常是物件引用。一般地,如果你想保護乙個類的例項,你可以使用this;如果你希望保護乙個靜態變數(如互斥**段在乙個靜態方法內部),一般使用類名就可以了。而statement_block就是互斥段的**,這段**在乙個時刻內只可能被乙個執行緒執行。

//lock.cs

using system;

using system.threading;

internal class account

 internal int withdraw(int amount)



//下面的**保證在當前執行緒修改balance的值完成之前

//不會有其他執行緒也執行這段**來修改balance的值

//因此,balance的值是不可能小於0的

lock (this)



else



}

}

internal void dotransactions()



}

internal class test

 for (int i = 0; i < 10; i++)

threads[i].name=i.tostring();

for (int i = 0; i < 10; i++)

threads[i].start();

console.readline();

}

}而多執行緒公用乙個物件時,也會出現和公用**類似的問題,這種問題就不應該使用lock關鍵字了,這裡需要用到system.threading中的乙個類monitor,我們可以稱之為監視器,monitor提供了使執行緒共享資源的方案。

monitor類可以鎖定乙個物件,乙個執行緒只有得到這把鎖才可以對該物件進行操作。物件鎖機制保證了在可能引起混亂的情況下乙個時刻只有乙個執行緒可以訪問這個物件。monitor必須和乙個具體的物件相關聯,但是由於它是乙個靜態的類,所以不能使用它來定義物件,而且它的所有方法都是靜態的,不能使用物件來引用。下面**說明了使用monitor鎖定乙個物件的情形:

......

queue oqueue=new queue();

......

monitor.enter(oqueue);

......//現在oqueue物件只能被當前執行緒操縱了

monitor.exit(oqueue);//釋放鎖

如上所示,當乙個執行緒呼叫monitor.enter()方法鎖定乙個物件時,這個物件就歸它所有了,其它執行緒想要訪問這個物件,只有等待它使用monitor.exit()方法釋放鎖。為了保證執行緒最終都能釋放鎖,你可以把monitor.exit()方法寫在try-catch-finally結構中的finally**塊裡。對於任何乙個被monitor鎖定的物件,記憶體中都儲存著與它相關的一些資訊,其一是現在持有鎖的執行緒的引用,其二是乙個預備隊列,佇列中儲存了已經準備好獲取鎖的執行緒,其三是乙個等待佇列,佇列中儲存著當前正在等待這個物件狀態改變的佇列的引用。當擁有物件鎖的執行緒準備釋放鎖時,它使用monitor.pulse()方法通知等待佇列中的第乙個執行緒,於是該執行緒被轉移到預備隊列中,當物件鎖被釋放時,在預備隊列中的執行緒可以立即獲得物件鎖。

下面是乙個展示如何使用lock關鍵字和monitor類來實現執行緒的同步和通訊的例子,也是乙個典型的生產者與消費者問題。這個例程中,生產者執行緒和消費者執行緒是交替進行的,生產者寫入乙個數,消費者立即讀取並且顯示,我將在注釋中介紹該程式的精要所在。用到的系統命名空間如下:

using system;

using system.threading

C 的多執行緒機制初探 2

threadtest.cs using system using system.threading namespace threadtest public class catch threadstateexception return 0 這段程式包含兩個類alpha和 在建立執行緒othread時...

C 的多執行緒機制初探 5

三.執行緒的同步和通訊 生產者和消費者 假設這樣一種情況,兩個執行緒同時維護乙個佇列,如果乙個執行緒對佇列中新增元素,而另外乙個執行緒從佇列中取用元素,那麼我們稱新增元素的執行緒為生產者,稱取用元素的執行緒為消費者。生產者與消費者問題看起來很簡單,但是卻是多執行緒應用中乙個必須解決的問題,它涉及到執...

C 的多執行緒機制初探 7

然後在下面這個類monitorsample的main 函式中我們要做的就是建立兩個執行緒分別作為生產者和消費者,使用cellprod.threadrun 方法和cellcons.threadrun 方法對同乙個cell物件進行操作。public class monitorsample catch t...