国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

c#多線程同步(轉(zhuǎn)載)

2019-11-17 03:10:18
字體:
供稿:網(wǎng)友

c#多線程同步(轉(zhuǎn)載)

我們在編程的時(shí)候,有時(shí)會(huì)使用多線程來解決問題,比如你的程序需要在后臺(tái)處理一大堆數(shù)據(jù),但還要使用戶界面處于可操作狀態(tài);或者你的程序需要訪問一些外部資源如數(shù)據(jù)庫或網(wǎng)絡(luò)文件等。這些情況你都可以創(chuàng)建一個(gè)子線程去處理,然而,多線程不可避免地會(huì)帶來一個(gè)問題,就是線程同步的問題。如果這個(gè)問題處理不好,我們就會(huì)得到一些非預(yù)期的結(jié)果。

  在網(wǎng)上也看過一些關(guān)于線程同步的文章,其實(shí)線程同步有好幾種方法,下面我就簡單的做一下歸納。

  一、volatile關(guān)鍵字

  volatile是最簡單的一種同步方法,當(dāng)然簡單是要付出代價(jià)的。它只能在變量一級(jí)做同步,volatile的含義就是告訴處理器, 不要將我放入工作內(nèi)存, 請(qǐng)直接在主存操作我。(【轉(zhuǎn)自www.bitsCN.com】)因此,當(dāng)多線程同時(shí)訪問該變量時(shí),都將直接操作主存,從本質(zhì)上做到了變量共享。

  能夠被標(biāo)識(shí)為volatile的必須是以下幾種類型:(摘自MSDN)

  • Any reference type.
  • Any pointer type (in an unsafe context).
  • The types sbyte, byte, short, ushort, int, uint, char, float, bool.
  • An enum type with an enum base type of byte, sbyte, short, ushort, int, or uint.

  如:

復(fù)制代碼CodepublicclassA{PRivatevolatileint_i;publicintI{get{return_i; }set{ _i=value; }}}
復(fù)制代碼

  但volatile并不能實(shí)現(xiàn)真正的同步,因?yàn)樗牟僮骷?jí)別只停留在變量級(jí)別,而不是原子級(jí)別。如果是在單處理器系統(tǒng)中,是沒有任何問題的,變量在主存中沒有機(jī)會(huì)被其他人修改,因?yàn)橹挥幸粋€(gè)處理器,這就叫作processor Self-Consistency。但在多處理器系統(tǒng)中,可能就會(huì)有問題。每個(gè)處理器都有自己的data cach,而且被更新的數(shù)據(jù)也不一定會(huì)立即寫回到主存。所以可能會(huì)造成不同步,但這種情況很難發(fā)生,因?yàn)閏ach的讀寫速度相當(dāng)快,flush的頻率也相當(dāng)高,只有在壓力測試的時(shí)候才有可能發(fā)生,而且?guī)茁史浅7浅P ?/p>

  二、lock關(guān)鍵字

  lock是一種比較好用的簡單的線程同步方式,它是通過為給定對(duì)象獲取互斥鎖來實(shí)現(xiàn)同步的。它可以保證當(dāng)一個(gè)線程在關(guān)鍵代碼段的時(shí)候,另一個(gè)線程不會(huì)進(jìn)來,它只能等待,等到那個(gè)線程對(duì)象被釋放,也就是說線程出了臨界區(qū)。用法:

復(fù)制代碼CodepublicvoidFunction(){objectlockThis=newobject();lock(lockThis){//access thread-sensitive resources.}}
復(fù)制代碼

  lock的參數(shù)必須是基于引用類型的對(duì)象,不要是基本類型像bool,int什么的,這樣根本不能同步,原因是lock的參數(shù)要求是對(duì)象,如果傳入int,勢必要發(fā)生裝箱操作,這樣每次lock的都將是一個(gè)新的不同的對(duì)象。最好避免使用public類型或不受程序控制的對(duì)象實(shí)例,因?yàn)檫@樣很可能導(dǎo)致死鎖。特別是不要使用字符串作為lock的參數(shù),因?yàn)樽址籆LR“暫留”,就是說整個(gè)應(yīng)用程序中給定的字符串都只有一個(gè)實(shí)例,因此更容易造成死鎖現(xiàn)象。建議使用不被“暫留”的私有或受保護(hù)成員作為參數(shù)。其實(shí)某些類已經(jīng)提供了專門用于被鎖的成員,比如Array類型提供SyncRoot,許多其它集合類型也都提供了SyncRoot。

  所以,使用lock應(yīng)該注意以下幾點(diǎn): 

  1、如果一個(gè)類的實(shí)例是public的,最好不要lock(this)。因?yàn)槭褂媚愕念惖娜艘苍S不知道你用了lock,如果他new了一個(gè)實(shí)例,并且對(duì)這個(gè)實(shí)例上鎖,就很容易造成死鎖。

  2、如果MyType是public的,不要lock(typeof(MyType))

  3、永遠(yuǎn)也不要lock一個(gè)字符串

  三、System.Threading.Interlocked

  對(duì)于整數(shù)數(shù)據(jù)類型的簡單操作,可以用Interlocked類的成員來實(shí)現(xiàn)線程同步,存在于System.Threading命名空間。Interlocked類有以下方法:Increment,Decrement,Exchange和CompareExchange。使用Increment和Decrement可以保證對(duì)一個(gè)整數(shù)的加減為一個(gè)原子操作。Exchange方法自動(dòng)交換指定變量的值。CompareExchange方法組合了兩個(gè)操作:比較兩個(gè)值以及根據(jù)比較的結(jié)果將第三個(gè)值存儲(chǔ)在其中一個(gè)變量中。比較和交換操作也是按原子操作執(zhí)行的。如:

復(fù)制代碼Codeinti=0;System.Threading.Interlocked.Increment(refi);Console.WriteLine(i);System.Threading.Interlocked.Decrement(refi);Console.WriteLine(i);System.Threading.Interlocked.Exchange(refi,100);Console.WriteLine(i);System.Threading.Interlocked.CompareExchange(refi,10,100);
復(fù)制代碼

Output:

  四、Monitor

  Monitor類提供了與lock類似的功能,不過與lock不同的是,它能更好的控制同步塊,當(dāng)調(diào)用了Monitor的Enter(Object o)方法時(shí),會(huì)獲取o的獨(dú)占權(quán),直到調(diào)用Exit(Object o)方法時(shí),才會(huì)釋放對(duì)o的獨(dú)占權(quán),可以多次調(diào)用Enter(Object o)方法,只需要調(diào)用同樣次數(shù)的Exit(Object o)方法即可,Monitor類同時(shí)提供了TryEnter(Object o,[int])的一個(gè)重載方法,該方法嘗試獲取o對(duì)象的獨(dú)占權(quán),當(dāng)獲取獨(dú)占權(quán)失敗時(shí),將返回false。

  但使用lock通常比直接使用Monitor更可取,一方面是因?yàn)?strong>lock更簡潔,另一方面是因?yàn)?strong>lock確保了即使受保護(hù)的代碼引發(fā)異常,也可以釋放基礎(chǔ)監(jiān)視器。這是通過finally中調(diào)用Exit來實(shí)現(xiàn)的。事實(shí)上,lock就是用Monitor類來實(shí)現(xiàn)的。下面兩段代碼是等效的:

復(fù)制代碼Codelock(x){DoSomething();}
等效于objectobj=(object)x;System.Threading.Monitor.Enter(obj);try{DoSomething();}finally{System.Threading.Monitor.Exit(obj);}
復(fù)制代碼

關(guān)于用法,請(qǐng)參考下面的代碼:

復(fù)制代碼Codeprivatestaticobjectm_monitorObject=newobject();[STAThread]staticvoidMain(string[] args){Thread thread=newThread(newThreadStart(Do));thread.Name="Thread1";Thread thread2=newThread(newThreadStart(Do));thread2.Name="Thread2";thread.Start();thread2.Start();thread.Join();thread2.Join();Console.Read();}staticvoidDo(){if(!Monitor.TryEnter(m_monitorObject)){Console.WriteLine("Can't visit Object"+Thread.CurrentThread.Name);return;}try{Monitor.Enter(m_monitorObject);Console.WriteLine("Enter Monitor"+Thread.CurrentThread.Name);Thread.Sleep(5000);}finally{Monitor.Exit(m_monitorObject);}}
復(fù)制代碼

  當(dāng)線程1獲取了m_monitorObject對(duì)象獨(dú)占權(quán)時(shí),線程2嘗試調(diào)用TryEnter(m_monitorObject),此時(shí)會(huì)由于無法獲取獨(dú)占權(quán)而返回false,輸出信息如下:

  另外,Monitor還提供了三個(gè)靜態(tài)方法Monitor.Pulse(Object o),Monitor.PulseAll(Object o)和Monitor.Wait(Object o ) ,用來實(shí)現(xiàn)一種喚醒機(jī)制的同步。關(guān)于這三個(gè)方法的用法,可以參考MSDN,這里就不詳述了。

  五、Mutex

  在使用上,Mutex與上述的Monitor比較接近,不過Mutex不具備Wait,Pulse,PulseAll的功能,因此,我們不能使用Mutex實(shí)現(xiàn)類似的喚醒的功能。不過Mutex有一個(gè)比較大的特點(diǎn),Mutex是跨進(jìn)程的,因此我們可以在同一臺(tái)機(jī)器甚至遠(yuǎn)程的機(jī)器上的多個(gè)進(jìn)程上使用同一個(gè)互斥體。盡管Mutex也可以實(shí)現(xiàn)進(jìn)程內(nèi)的線程同步,而且功能也更強(qiáng)大,但這種情況下,還是推薦使用Monitor,因?yàn)镸utex類是win32封裝的,所以它所需要的互操作轉(zhuǎn)換更耗資源。

  六、ReaderWriterLock

  在考慮資源訪問的時(shí)候,慣性上我們會(huì)對(duì)資源實(shí)施lock機(jī)制,但是在某些情況下,我們僅僅需要讀取資源的數(shù)據(jù),而不是修改資源的數(shù)據(jù),在這種情況下獲取資源的獨(dú)占權(quán)無疑會(huì)影響運(yùn)行效率,因此.Net提供了一種機(jī)制,使用ReaderWriterLock進(jìn)行資源訪問時(shí),如果在某一時(shí)刻資源并沒有獲取寫的獨(dú)占權(quán),那么可以獲得多個(gè)讀的訪問權(quán),單個(gè)寫入的獨(dú)占權(quán),如果某一時(shí)刻已經(jīng)獲取了寫入的獨(dú)占權(quán),那么其它讀取的訪問權(quán)必須進(jìn)行等待,參考以下代碼:

復(fù)制代碼CodeprivatestaticReaderWriterLock m_readerWriterLock=newReaderWriterLock();privatestaticintm_int=0;[STAThread]staticvoidMain(string[] args){Thread readThread=newThread(newThreadStart(Read));readThread.Name="ReadThread1";Thread readThread2=newThread(newThreadStart(Read));readThread2.Name="ReadThread2";Thread writeThread=newThread(newThreadStart(Writer));writeThread.Name="WriterThread";readThread.Start();readThread2.Start();writeThread.Start();readThread.Join();readThread2.Join();writeThread.Join();Console.ReadLine();}privatestaticvoidRead(){while(true){Console.WriteLine("ThreadName"+Thread.CurrentThread.Name+"AcquireReaderLock");m_readerWriterLock.AcquireReaderLock(10000);Console.WriteLine(String.Format("ThreadName : {0} m_int : {1}", Thread.CurrentThread.Name, m_int));m_readerWriterLock.ReleaseReaderLock();}}privatestaticvoidWriter(){while(true){Console.WriteLine("ThreadName"+Thread.CurrentThread.Name+"AcquireWriterLock");m_readerWriterLock.AcquireWriterLock(1000);Interlocked.Increment(refm_int);Thread.Sleep(5000);m_readerWriterLock.ReleaseWriterLock();Console.WriteLine("ThreadName"+Thread.CurrentThread.Name+"ReleaseWriterLock");}}
復(fù)制代碼

在程序中,我們啟動(dòng)兩個(gè)線程獲取m_int的讀取訪問權(quán),使用一個(gè)線程獲取m_int的寫入獨(dú)占權(quán),執(zhí)行代碼后,輸出如下:

可以看到,當(dāng)WriterThread獲取到寫入獨(dú)占權(quán)后,任何其它讀取的線程都必須等待,直到WriterThread釋放掉寫入獨(dú)占權(quán)后,才能獲取到數(shù)據(jù)的訪問權(quán),應(yīng)該注意的是,上述打印信息很明顯顯示出,可以多個(gè)線程同時(shí)獲取數(shù)據(jù)的讀取權(quán),這從ReadThread1和ReadThread2的信息交互輸出可以看出。

  七、SynchronizationAttribute

  當(dāng)我們確定某個(gè)類的實(shí)例在同一時(shí)刻只能被一個(gè)線程訪問時(shí),我們可以直接將類標(biāo)識(shí)成Synchronization的,這樣,CLR會(huì)自動(dòng)對(duì)這個(gè)類實(shí)施同步機(jī)制,實(shí)際上,這里面涉及到同步域的概念,當(dāng)類按如下設(shè)計(jì)時(shí),我們可以確保類的實(shí)例無法被多個(gè)線程同時(shí)訪問  1).在類的聲明中,添加System.Runtime.Remoting.Contexts.Synchronization

上一篇:C# 特性

下一篇:C# WinForm開發(fā)系列

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 雷波县| 石嘴山市| 宁化县| 右玉县| 天台县| 闸北区| 西乌珠穆沁旗| 凤冈县| 泰来县| 东兰县| 宝山区| 夹江县| 神池县| 武夷山市| 龙州县| 阿鲁科尔沁旗| 东台市| 朔州市| 扶绥县| 西藏| 珲春市| 诏安县| 乌什县| 政和县| 宣恩县| 河东区| 平南县| 乌兰察布市| 甘肃省| 普洱| 泾源县| 林州市| 岳池县| 合川市| 图们市| 云梦县| 伊宁市| 高碑店市| 清远市| 海晏县| 呼和浩特市|