日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > C# >内容正文

C#

C# 多线程并发锁模式-总结

發(fā)布時(shí)間:2025/5/22 C# 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C# 多线程并发锁模式-总结 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

開(kāi)篇:

?

互斥還是lock Monitor Mutex 模式!

?

?

Muex Monitor lock AutoEventSet ManualEventSet?

后續(xù)的

ReaderWriterLock

ReaderWriterLockSlim 類

表示用于管理資源訪問(wèn)的鎖定狀態(tài),可實(shí)現(xiàn)多線程讀取或進(jìn)行獨(dú)占式寫入訪問(wèn)。

使用?ReaderWriterLockSlim?來(lái)保護(hù)由多個(gè)線程讀取但每次只采用一個(gè)線程寫入的資源。?ReaderWriterLockSlim?允許多個(gè)線程均處于讀取模式,允許一個(gè)線程處于寫入模式并獨(dú)占鎖定狀態(tài),同時(shí)還允許一個(gè)具有讀取權(quán)限的線程處于可升級(jí)的讀取模式,在此模式下線程無(wú)需放棄對(duì)資源的讀取權(quán)限即可升級(jí)為寫入模式。

注意?ReaderWriterLockSlim 類似于 ReaderWriterLock,只是簡(jiǎn)化了遞歸、升級(jí)和降級(jí)鎖定狀態(tài)的規(guī)則。 ReaderWriterLockSlim 可避免多種潛在的死鎖情況。 此外,ReaderWriterLockSlim 的性能明顯優(yōu)于 ReaderWriterLock。 建議在所有新的開(kāi)發(fā)工作中使用 ReaderWriterLockSlim。

以上引用自MSDN

?

?

C# 線程手冊(cè) 第三章 使用線程 ReaderWriterLock 類

2012-02-07 21:53 by DanielWise,?4166?閱讀,?1?評(píng)論,?收藏,?編輯

一個(gè)ReaderWriterLock 類定義一個(gè)實(shí)現(xiàn)單寫多讀語(yǔ)義的鎖。這個(gè)類通常用在能被多個(gè)線程讀取但是僅能被一個(gè)線程寫入的文件操作時(shí)使用。下面是ReaderWriterLock類中的四個(gè)主要方法:

? a. AcquireReaderLock(): 這個(gè)重載方法獲取一個(gè)讀者鎖,接受一個(gè)整型或者TimeSpan類型的timeout 值。timeout是一個(gè)檢測(cè)死鎖的利器。

? b. AcquireWriterLock():? 這個(gè)重載方法獲取一個(gè)寫者鎖,接受一個(gè)整型或者TimeSpan類型的timeout 值。

? c. ReleaseReaderLock(): 釋放讀者鎖。

? d. ReleaseWriterLock(): 釋放寫者鎖。

?

使用ReaderWriterLock類可以讓多線程安全地進(jìn)行數(shù)據(jù)并發(fā)讀取。只有當(dāng)線程正在更新的數(shù)據(jù)鎖定。讀者線程可以再?zèng)]有寫者擁有鎖的時(shí)候獲得鎖。寫者線程可以再?zèng)]有讀者線程或者寫者線程擁有鎖的時(shí)候獲得鎖。

下面的列表ReadeWriteLock.cs, 描述了如何使用ReaderWriterLock()鎖:

/************************************* /* copyright (c) 2012 daniel dong* * author:daniel dong* blog: www.cnblogs.com/danielwise* email: guofoo@163.com* */using System; using System.Collections.Generic; using System.Text; using System.Threading;namespace ReadWriteLock {public class ReadWrite{private ReaderWriterLock rwl;private int x;private int y;public ReadWrite(){rwl = new ReaderWriterLock();}public void ReadInts(ref int a, ref int b){rwl.AcquireReaderLock(Timeout.Infinite);try{a = this.x;b = this.y;}finally{rwl.ReleaseReaderLock();}}public void WriteInts(int a, int b){rwl.AcquireWriterLock(Timeout.Infinite);try{this.x = a;this.y = b;Console.WriteLine("x = " + this.x+ " y = " + this.y+ " ThreadID = " + Thread.CurrentThread.GetHashCode());}finally{rwl.ReleaseWriterLock();}}}public class RWApp{private ReadWrite rw = new ReadWrite();public static void Main(string[] args){RWApp e = new RWApp();//Writer ThreadsThread wt1 = new Thread(new ThreadStart(e.Write));wt1.Start();Thread wt2 = new Thread(new ThreadStart(e.Write));wt2.Start();//Reader ThreadsThread rt1 = new Thread(new ThreadStart(e.Read));rt1.Start();Thread rt2 = new Thread(new ThreadStart(e.Read));rt2.Start();Console.ReadLine();}private void Write(){int a = 10;int b = 11;Console.WriteLine("************** Write *************");for (int i = 0; i < 5; i++){this.rw.WriteInts(a++, b++);Thread.Sleep(1000);}}private void Read(){int a = 10;int b = 11;Console.WriteLine("************** Read *************");for (int i = 0; i < 5; i++){this.rw.ReadInts(ref a, ref b);Console.WriteLine("For i = " + i+ " a = " + a+ " b = " + b+ " TheadID = " + Thread.CurrentThread.GetHashCode());Thread.Sleep(1000);}}} }

ReadWriteLock 的輸出結(jié)果可能與下表類似:

在上面的列表中,線程wt1 和 wt2 是WriteInts()方法中獲得寫鎖的寫者線程,線程rt1 和 rt2 是在ReadInts()方法中獲得讀者鎖的讀者線程。在WriteInts()方法中,變量x 和 y 的值分別被改成a 和 b. 當(dāng)線程wt1 或 wt2 通過(guò)調(diào)用AcquireWriterLock() 方法獲得一個(gè)寫者鎖后,那么直到這個(gè)線程通過(guò)調(diào)用ReleaseWriterLock()方法釋放鎖之前任何其他線程(包括讀者線程rt1 和 rt2)都不被允許訪問(wèn)相應(yīng)對(duì)象。這個(gè)行為與Monitors類似 。在ReadInts()方法中,線程rt1 和 rt2 通過(guò)調(diào)用AcquireReaderLock()方法獲得讀者鎖, 這兩個(gè)線程可以并發(fā)地訪問(wèn)變量x 和 y. 直到讀者線程釋放它們的讀者鎖以后,寫者線程(wt1 和 wt2)才被允許訪問(wèn)對(duì)應(yīng)對(duì)象。只有讀者線程在獲得讀者鎖以后才可以并發(fā)地訪問(wèn)。

Monitors類對(duì)于只想來(lái)讀數(shù)據(jù)而非寫數(shù)據(jù)來(lái)說(shuō)過(guò)于“安全”了。Monitors 也有一些性能問(wèn)題,對(duì)于只讀類型的訪問(wèn)來(lái)說(shuō),性能瓶頸是可以避免的。ReaderWriterLock類通過(guò)允許任意數(shù)量的線程并發(fā)地讀取數(shù)據(jù)來(lái)提供一個(gè)解決數(shù)據(jù)讀-寫問(wèn)題的完美方案。當(dāng)線程更新數(shù)據(jù)時(shí)鎖住數(shù)據(jù)。當(dāng)沒(méi)有寫者線程擁有鎖的時(shí)候?qū)懻呔€程可以獲得鎖。寫者鎖可以在沒(méi)有讀者線程或者寫者線程擁有鎖的時(shí)候獲得鎖。因此,ReaderWriterLock 就像是一段關(guān)鍵部分代碼, 它也支持一個(gè)timeout 值,而這方面在檢測(cè)死鎖時(shí)非常有用。

?

?

?

??????1.幾種同步方法的區(qū)別

??????lock和Monitor是.NET用一個(gè)特殊結(jié)構(gòu)實(shí)現(xiàn)的,Monitor對(duì)象是完全托管的、完全可移植的,并且在操作系統(tǒng)資源要求方 面可能更為有效,同步速度較快,但不能跨進(jìn)程同步。lock(Monitor.Enter和Monitor.Exit方法的封裝),主要作用是鎖定臨界區(qū),使臨 界區(qū)代碼只能被獲得鎖的線程執(zhí)行。Monitor.Wait和Monitor.Pulse用于線程同步,類似信號(hào)操作,個(gè)人感覺(jué)使用比較復(fù)雜,容易造成死 鎖。

??????互斥體Mutex和事件對(duì)象EventWaitHandler屬于內(nèi)核對(duì)象,利用內(nèi)核對(duì)象進(jìn)行線程同步,線程必須要在用戶模式和內(nèi)核模 式間切換,所以一般效率很低,但利用互斥對(duì)象和事件對(duì)象這樣的內(nèi)核對(duì)象,可以在多個(gè)進(jìn)程中的各個(gè)線程間進(jìn)行同步。

??????互斥體Mutex類似于一個(gè)接力棒,拿到接力棒的線程才可以開(kāi)始跑,當(dāng)然接力棒一次只屬于一個(gè)線程(Thread Affinity),如果這個(gè)線程不釋放接力棒(Mutex.ReleaseMutex),那么沒(méi)辦法,其他所有需要接力棒運(yùn)行的線程都知道能等著看熱 鬧。

??????EventWaitHandle 類允許線程通過(guò)發(fā)信號(hào)互相通信。 通常,一個(gè)或多個(gè)線程在 EventWaitHandle 上阻止,直到一個(gè)未阻止的線程調(diào)用 Set 方法,以釋放一個(gè)或多個(gè)被阻止的線程。

??????2.什么時(shí)候需要鎖定

??????首先要理解鎖定是解決競(jìng)爭(zhēng)條件的,也就是多個(gè)線程同時(shí)訪問(wèn)某個(gè)資源,造成意想不到的結(jié)果。比如,最簡(jiǎn)單的情況是,一個(gè)計(jì)數(shù)器,兩個(gè)線程 同時(shí)加一,后果就是損失了一個(gè)計(jì)數(shù),但相當(dāng)頻繁的鎖定又可能帶來(lái)性能上的消耗,還有最可怕的情況死鎖。那么什么情況下我們需要使用鎖,什么情況下不需要 呢?

??????1)只有共享資源才需要鎖定
??????只有可以被多線程訪問(wèn)的共享資源才需要考慮鎖定,比如靜態(tài)變量,再比如某些緩存中的值,而屬于線程內(nèi)部的變量不需要鎖定。?

??????2)多使用lock,少用Mutex
????? 如果你一定要使用鎖定,請(qǐng)盡量不要使用內(nèi)核模塊的鎖定機(jī)制,比如.NET的Mutex,Semaphore,AutoResetEvent和 ManuResetEvent,使用這樣的機(jī)制涉及到了系統(tǒng)在用戶模式和內(nèi)核模式間的切換,性能差很多,但是他們的優(yōu)點(diǎn)是可以跨進(jìn)程同步線程,所以應(yīng)該清 楚的了解到他們的不同和適用范圍。

????? 3)了解你的程序是怎么運(yùn)行的
????? 實(shí)際上在web開(kāi)發(fā)中大多數(shù)邏輯都是在單個(gè)線程中展開(kāi)的,一個(gè)請(qǐng)求都會(huì)在一個(gè)單獨(dú)的線程中處理,其中的大部分變量都是屬于這個(gè)線程的,根本沒(méi)有必要考慮鎖 定,當(dāng)然對(duì)于ASP.NET中的Application對(duì)象中的數(shù)據(jù),我們就要考慮加鎖了。

????? 4)把鎖定交給數(shù)據(jù)庫(kù)
????? 數(shù) 據(jù)庫(kù)除了存儲(chǔ)數(shù)據(jù)之外,還有一個(gè)重要的用途就是同步,數(shù)據(jù)庫(kù)本身用了一套復(fù)雜的機(jī)制來(lái)保證數(shù)據(jù)的可靠和一致性,這就為我們節(jié)省了很多的精力。保證了數(shù)據(jù)源 頭上的同步,我們多數(shù)的精力就可以集中在緩存等其他一些資源的同步訪問(wèn)上了。通常,只有涉及到多個(gè)線程修改數(shù)據(jù)庫(kù)中同一條記錄時(shí),我們才考慮加鎖。?

????? 5)業(yè)務(wù)邏輯對(duì)事務(wù)和線程安全的要求
????? 這 條是最根本的東西,開(kāi)發(fā)完全線程安全的程序是件很費(fèi)時(shí)費(fèi)力的事情,在電子商務(wù)等涉及金融系統(tǒng)的案例中,許多邏輯都必須嚴(yán)格的線程安全,所以我們不得不犧牲 一些性能,和很多的開(kāi)發(fā)時(shí)間來(lái)做這方面的工作。而一般的應(yīng)用中,許多情況下雖然程序有競(jìng)爭(zhēng)的危險(xiǎn),我們還是可以不使用鎖定,比如有的時(shí)候計(jì)數(shù)器少一多一, 對(duì)結(jié)果無(wú)傷大雅的情況下,我們就可以不用去管它。

??????3.InterLocked類

????? Interlocked 類提供了同步對(duì)多個(gè)線程共享的變量的訪問(wèn)的方法。如果該變量位于共享內(nèi)存中,則不同進(jìn)程的線程就可以使用該機(jī)制。互鎖操作是原子的,即整個(gè)操作是不能由相 同變量上的另一個(gè)互鎖操作所中斷的單元。這在搶先多線程操作系統(tǒng)中是很重要的,在這樣的操作系統(tǒng)中,線程可以在從某個(gè)內(nèi)存地址加載值之后但是在有機(jī)會(huì)更改 和存儲(chǔ)該值之前被掛起。

????? 我們來(lái)看一個(gè)InterLock.Increment()的例子,該方法以原子的形式遞增指定變量并存儲(chǔ)結(jié)果,示例如下:


????class?InterLockedTest
????{
????????public?static?Int64?i?=?0;

????????public?static?void?Add()
????????{
????????????for?(int?i?=?0;?i?<?100000000;?i++)
????????????{
????????????????Interlocked.Increment(ref?InterLockedTest.i);
????????????????//InterLockedTest.i?=?InterLockedTest.i?+?1;
????????????}
????????}


????????public?static?void?Main(string[]?args)
????????{
????????????Thread?t1?=?new?Thread(new?ThreadStart(InterLockedTest.Add));
????????????Thread?t2?=?new?Thread(new?ThreadStart(InterLockedTest.Add));

????????????t1.Start();
????????????t2.Start();

????????????t1.Join();
????????????t2.Join();

????????????Console.WriteLine(InterLockedTest.i.ToString());
????????????Console.Read();
????????}
????}

????? 輸出結(jié)果200000000,如果InterLockedTest.Add()方法中用注釋掉的語(yǔ)句代替Interlocked.Increment() 方法,結(jié)果將不可預(yù)知,每次執(zhí)行結(jié)果不同。InterLockedTest.Add()方法保證了加1操作的原子性,功能上相當(dāng)于自動(dòng)給加操作使用了 lock鎖。同時(shí)我們也注意到InterLockedTest.Add()用時(shí)比直接用+號(hào)加1要耗時(shí)的多,所以說(shuō)加鎖資源損耗還是很明顯的。

????? 另外InterLockedTest類還有幾個(gè)常用方法,具體用法可以參考MSDN上的介紹。

???4.集合類的同步

????? .NET在一些集合類,比如Queue、ArrayList、HashTable和Stack,已經(jīng)提供了一個(gè)供lock使用的對(duì)象SyncRoot。用 Reflector查看了SyncRoot屬性(Stack.SynchRoot略有不同)的源碼如下:


public?virtual?object?SyncRoot
{
????get
????{
????????if?(this._syncRoot?==?null)
????????{
????????????//如果_syncRoot和null相等,將new?object賦值給 _syncRoot
????????????//Interlocked.CompareExchange方法保證多個(gè)線程在使用 syncRoot時(shí)是線程安全的
????????????Interlocked.CompareExchange(ref?this._syncRoot,?new?object(),?null);
????????}
????????return?this._syncRoot;
????}
}

????? 這里要特別注意的是MSDN提到:從頭到尾對(duì)一個(gè)集合進(jìn)行枚舉本質(zhì)上并不是一個(gè)線程安全的過(guò)程。即使一個(gè)集合已進(jìn)行同步,其他線程仍可以修改該集合,這將 導(dǎo)致枚舉數(shù)引發(fā)異常。若要在枚舉過(guò)程中保證線程安全,可以在整個(gè)枚舉過(guò)程中鎖定集合,或者捕捉由于其他線程進(jìn)行的更改而引發(fā)的異常。應(yīng)該使用下面的代碼:

Queue使用lock示例

????? 還有一點(diǎn)需要說(shuō)明的是,集合類提供了一個(gè)是和同步相關(guān)的方法Synchronized,該 方法返回一個(gè)對(duì)應(yīng)的集合類的wrapper類,該類是線程安全的,因?yàn)樗拇蟛糠址椒ǘ加胠ock關(guān)鍵字進(jìn)行了同步處理。如HashTable的 Synchronized返回一個(gè)新的線程安全的HashTable實(shí)例,代碼如下:


????//在多線程環(huán)境中只要我們用下面的 方式實(shí)例化HashTable就可以了
????Hashtable?ht?=?Hashtable.Synchronized(new?Hashtable());

????//以下代碼是.NET?Framework?Class?Library實(shí)現(xiàn),增加對(duì) Synchronized的認(rèn)識(shí)
????[HostProtection(SecurityAction.LinkDemand,?Synchronization=true)]
????public?static?Hashtable?Synchronized(Hashtable?table)
????{
????????if?(table?==?null)
????????{
????????????throw?new?ArgumentNullException("table");
????????}
????????return?new?SyncHashtable(table);
????}

?
????//SyncHashtable的幾個(gè)常用方法,我們可以看到內(nèi)部實(shí)現(xiàn)都加了lock關(guān)鍵字 保證線程安全
????public?override?void?Add(object?key,?object?value)
????{
????????lock?(this._table.SyncRoot)
????????{
????????????this._table.Add(key,?value);
????????}
????}

????public?override?void?Clear()
????{
????????lock?(this._table.SyncRoot)
????????{
????????????this._table.Clear();
????????}
????}

????public?override?void?Remove(object?key)
????{
????????lock?(this._table.SyncRoot)
????????{
????????????this._table.Remove(key);
????????}
????}

????? 線程同步是一個(gè)非常復(fù)雜的話題,這里只是根據(jù)公司的一個(gè)項(xiàng)目把相關(guān)的知識(shí)整理出來(lái),作為工作的一種總結(jié)。這些同步方法的使用場(chǎng)景是怎樣的?究竟有哪些細(xì)微 的差別?還有待于進(jìn)一步的學(xué)習(xí)和實(shí)踐。

總結(jié)

以上是生活随笔為你收集整理的C# 多线程并发锁模式-总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。