多線程 C#解決方案小結
與多線程相關的兩個常見的需要解決的問題是:臨界資源保護和線程間的同步依賴,每一種語言都提供了自己的一套設施(有的語言可能需要藉助OS的API)來解決這兩個問題,C#提供了更方便靈活的解決方案,首先C#可以允許我們在不同的層級上加鎖,也就是說我們可以控制加鎖的粒度。其次,C#提供了一套內建的安全執行緒的容器,方便我們的使用。
一.不同層級(Level)上的同步:
1.object level 同步
對應的class必須從ContextBoundObject繼承(同步上下文context,使所有的方法調用能被截獲),並且在
class上運用SynchronizationAttribute 。
2.Method level 同步
System.Runtime.CompilerService空間包含的一些屬性將影響CLR在運行期間的行為。特性MethodImplAttribute可以用於需要進行同步控制的方法上。
3.code segment level 同步
(1)Monitor類(主要是靜態方法)
Monitor.Enter(obj)//獲得加在對象obj上的鎖
...
Monitor.Exit(obj)//釋放鎖
//上面兩句之間的代碼相當於lock(obj){...}
Monitor.TryEnter(obj)//該方法立即返回,如果傳回值為false,則接下來不需要Monitor.Exit(obj)。
//以下幾個方法用於線程間的互動 ==》 解決同步依賴
Monitor.Wait(obj)//等待脈衝訊息。釋放對象上的鎖並阻塞當前線程,以後只有其它線程調用Pulse或PulseAll時才會給它再次獲得鎖的機會
Monitor.Pulse(obj)//發射脈衝訊息( 只有得到鎖後才能發射,而且發射不會自動釋放鎖)
Monitor.PulseAll(obj)
注意:
(1)Monitor 鎖定對象,只能在Enter()和Exit()之間的代碼塊中調用Wait和Pulse
(2)不能在一個線程中獲得鎖,而在另一個線程中釋放鎖。這樣會產生鎖丟失。 獲得鎖和釋放鎖應該在同一個線程中完成。
(3)lock語句
lock(obj)
{
需要進行同步的代碼
}
(4)ReaderWriterLock類
實現單寫多讀程式的鎖。
AcquireReaderLock()//當沒有寫程式線程佔用鎖時,就可獲得鎖
AcquireWriterLock()//當沒有任何讀寫程式線程佔用鎖時,才可獲得鎖
ReleaseReaderLock()
ReleaseWriterLock()
(5)ManualResetEvent
Set()方法將狀態設定為有訊號
Reset()將其設定為無訊號
WaitOne()將阻塞到其有訊號為止,若調用WaitOne的時刻就是有訊號的,將不會阻塞
(6)AutoResetEvent
與ManualResetEvent的區別是,AutoResetEvent.WaitOne()會自動改變事件對象的狀態,即AutoResetEvent.WaitOne()每執行一次,事件的狀態就改變一次。有訊號-->無訊號;無訊號-->有訊號
說明:
(1)無論是Monitor還是lock、ReaderWriterLock都只對參考型別的對象有效,因為參考型別的對象有一個隱藏的sync#欄位,該欄位的作用就是作為加鎖的標記。
(2)上述的各種設施中,只有Monitor 和ManualResetEvent/AutoResetEvent 能解決線程間的同步依賴問題,而其它的設施主要用於解決臨界資源共用。
4.member level同步
(1)Interlocked類(主要是靜態方法)
同步一個由許多線程共用的變數。
Decrement(ref int);//使變數減1
Increment(ref int);//使變數加1
//以上兩個方法僅針對類int變數
Exchange(ref object, object);
(2)ThreadStaticAttribute
該特性用於修飾靜態變數,被該特性修飾的靜態變數在每個線程中都有自己的副本。
二.建立安全執行緒的對象
Hashtable h = Hashtable.Synchronized(new Hashtable()) ;
ArrayList等容器也提供類似操作。
C#多線程筆記(一) document.title="C#多線程筆記(一) - "+document.title
1.每個表單都有自己的都在不同的線程上運行,如果需要在表單之間互動,就需要線上程之間互動。
2.當線程Sleep時,系統就退出執行隊列一段時間,當睡眠結束時,系統會產生一個時鐘中斷,從而使線程回到執行隊列中,從而恢複線程的執行。
3.如果父線程先於子線程結束,那麼子線程將在父線程結束的同時被迫結束。Thread.Join()方法使父線程等待,直到子線程結束。 Abort()方法帶來的後果是不可恢複的終止線程。
4.起始線程可以稱之為主線程,如果所有的前台線程都停止了,那麼主線程可以終止,而所有的後台線程都將無條件終止。
後台線程跟前台線程只有一個區別,那就是後台線程不妨礙程式的終止。一旦一個進程所有的前台線程都終止後,CLR 將通過調用任意一個存活中的後台進程的Abort()方法來徹底終止進程。
6.掛起,睡眠(都可稱為--阻塞,暫停)
與Thread.Sleep 不同,Thread.Suspend 不會使線程立即停止執行。直到線程到達安全點之後它才可以將該線程掛起。如果線程尚
未啟動或已經停止,則它將不能掛起。調用 Thread.Resume 將使另一個線程跳出掛起狀態並使該線程繼續執行。
一個線程不能對另一個線程調用Sleep ,但是一個線程可以對另一個線程調用Suspend。
還可以使用許多其它的方式來阻塞線程。例如,可以通過調用 Thread.Join 使一個線程等待另一個線程(子線程)停止。使用Monitor.Wait使一個線程等待訪問一個同步對象。
5.關鍵字lock可以把一段代碼定義為互斥段(critical section),互斥段在一個時刻內只允許一個線程進入執行,而其他線程必須等待。
多線程公用一個對象時,就不應該使用lock關鍵字了,這裡Monitor,Monitor提供了使線程共用資源的方案。
Monitor類可以鎖定一個對象,一個線程只有得到這把鎖才可以對該對象進行操作。
如:
Monitor.Enter(obj);
//現在oQueue對象只能被當前線程操縱了
Monitor.Exit(obj);
6.一個進程開始時至少會有一個主線程 ( 即主執行執行個體 ) ,這是在系統載入你的程式的時候所建立的主執行流程。
而訊息佇列則是與線程 ( Thread ) 相關的,在似win2k上一個線程有一個且只有一個訊息佇列 ( queue ) 與之相對應。
訊息佇列是在什麼時候產生的呢? 在似win2k系統上,從一開始建立線程就已經有了。
一個線程可以建立多個表單。統發送給這些視窗的訊息都統一發送到同一個 訊息佇列 中,幸虧訊息結構中有msg.hwnd指出該條訊息與 哪一個視窗相關, DispatchMessage() 函數就是依照這個保證訊息指派處理自動化而且不會出錯!
7.每個表單都屬於建立它的線程,在一線程中直接訪或間接問其它線程中的表單的方法將導致執行階段錯誤(VS2005)。
解決方案:使用表單從Control繼承而來的Control.Invoke(Delegate)方法。該方法將在建立表單的線程上執行委託指向的方法。
注意:在VS2003下,可以在一個線程中直接或間接調用另一個線程中的表單的方法,而不會導致執行階段錯誤。
本文引用地址: http://blog.csdn.net/zhuweisky/services/trackbacks/429826.aspx