日韩欧美国产精品免费一二-日韩欧美国产精品亚洲二区-日韩欧美国产精品专区-日韩欧美国产另-日韩欧美国产免费看-日韩欧美国产免费看清风阁

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

.NET中各種線程同步鎖

freeflydom
2024年8月18日 19:17 本文熱度 1404

編程編的久了,總會(huì)遇到多線程的情況,有些時(shí)候我們要幾個(gè)線程合作完成某些功能,這時(shí)候可以定義一個(gè)全局對(duì)象,各個(gè)線程根據(jù)這個(gè)對(duì)象的狀態(tài)來(lái)協(xié)同工作,這就是基本的線程同步

支持多線程編程的語(yǔ)言一般都內(nèi)置了一些類型和方法用于創(chuàng)建上述所說(shuō)的全局對(duì)象也就是鎖對(duì)象,它們的作用類似,使用場(chǎng)景有所不同。.Net中這玩意兒有很多,若不是經(jīng)常使用,我想沒人能完全記住它們各自的用法和相互的區(qū)別。為了便于查閱,現(xiàn)將它們記錄在此。

ps:本文雖然關(guān)注 .Net 平臺(tái),但涉及到的大部分鎖概念都是平臺(tái)無(wú)關(guān)的,在很多其它語(yǔ)言(如_Java__)中都能找到對(duì)應(yīng)。_

volatile 關(guān)鍵字#

確切地說(shuō),volatile 并不屬于鎖的范疇,但其背后蘊(yùn)藏著多線程的基本概念,有時(shí)人們也使用它實(shí)現(xiàn)自定義鎖。

緩存一致性#

了解volatile,首先要了解.Net/Java的內(nèi)存模型(.Net 當(dāng)年是諸多借鑒了 Java 的設(shè)計(jì)理念)。而 Java 內(nèi)存模型又借鑒了硬件層面的設(shè)計(jì)。

我們知道,在現(xiàn)代計(jì)算機(jī)中,處理器的指令速度遠(yuǎn)超內(nèi)存的存取速度,所以現(xiàn)代計(jì)算機(jī)系統(tǒng)都不得不加入一層讀寫速度盡可能接近處理器運(yùn)算速度的高速緩存來(lái)作為主存與處理器之間的緩沖。處理器計(jì)算直接存取的是高速緩存中的數(shù)據(jù),計(jì)算完畢后再同步到主存中。

在多處理器系統(tǒng)中,每個(gè)處理器都有自己的高速緩存,而它們又共享同一主存。

而 Java 內(nèi)存模型的每個(gè)線程有自己的工作內(nèi)存,其中保留了被線程使用的變量的副本。線程對(duì)變量的所有的操作都必須在工作內(nèi)存中完成,而不能直接讀寫主內(nèi)存中的變量。不同線程之間也不能直接訪問對(duì)方工作內(nèi)存中的變量,線程間變量的值的傳遞需要通過主內(nèi)存中轉(zhuǎn)來(lái)完成。

雖然兩者的設(shè)計(jì)相似,但是前者主要解決存取效率不匹配的問題,而后者主要解決內(nèi)存安全(競(jìng)爭(zhēng)、泄露)方面的問題。顯而易見,這種設(shè)計(jì)方案引入了新的問題——緩存一致性(CacheCoherence)——即各工作內(nèi)存、工作內(nèi)存與主存,它們存儲(chǔ)的相同變量對(duì)應(yīng)的值可能不一樣。


為了解決這個(gè)問題,很多平臺(tái)都內(nèi)置了 volatile 關(guān)鍵字,使用它修飾的變量,可以保證所有線程每次獲取到的是最新值。這是怎么做到的呢?這就要求所有線程在訪問變量時(shí)遵循預(yù)定的協(xié)議,比如MSI、MESI(IllinoisProtocol)、MOSI、Synapse、Firefly及DragonProtocol等,此處不贅述,只需要知道系統(tǒng)額外幫我們做了一些事情,多少會(huì)影響執(zhí)行效率。

另外 volatile 還能避免編譯器自作聰明重排指令。重排指令在大多數(shù)時(shí)候無(wú)傷大雅,還能對(duì)執(zhí)行效率有一定提升,但某些時(shí)候會(huì)影響到執(zhí)行結(jié)果,此時(shí)就可以使用 volatile。

Interlocked#

同 volatile 的可見性作用類似,Interlocked 可為多個(gè)線程共享的變量提供原子操作,這個(gè)類是一個(gè)靜態(tài)類,它提供了以線程安全的方式遞增、遞減、交換和讀取值的方法。

它的原子操作基于 CPU 本身,非阻塞,所以也不是真正意義上的鎖,當(dāng)然效率會(huì)比鎖高得多。


鎖模式#

接下來(lái)正式介紹各種鎖之前,先了解下鎖模式——鎖分為內(nèi)核模式鎖用戶模式鎖,后面也有了混合模式鎖

內(nèi)核模式就是在系統(tǒng)級(jí)別讓線程中斷,收到信號(hào)時(shí)再切回來(lái)繼續(xù)干活。該模式在線程掛起時(shí)由系統(tǒng)底層負(fù)責(zé),幾乎不占用 CPU 資源,但線程切換時(shí)效率低。

用戶模式就是通過一些 CPU 指令或者死循環(huán)讓線程一直運(yùn)行著直到可用。該模式下,線程掛起會(huì)一直占用 CPU 資源,但線程切換非常快。

長(zhǎng)時(shí)間的鎖定,優(yōu)先使用內(nèi)核模式鎖;如果有大量的鎖定,且鎖定時(shí)間非常短,切換頻繁,用戶模式鎖就很有用。另外內(nèi)核模式鎖可以實(shí)現(xiàn)跨進(jìn)程同步,而用戶模式鎖只能進(jìn)程內(nèi)同步

本文中,除文末輕量級(jí)同步原語(yǔ)為用戶模式鎖,其它鎖都為內(nèi)核模式。

lock 關(guān)鍵字#

lock 應(yīng)該是大多數(shù)開發(fā)人員最常用的鎖操作,此處不贅述。需要注意的是使用時(shí)應(yīng) lock 范圍盡量小,lock 時(shí)間盡量短,避免無(wú)謂等待。

Monitor#

上面 lock 就是Monitor的語(yǔ)法糖,通過編譯器編譯會(huì)生成 Monitor 的代碼,如下:

lock (syscRoot)
{
   //synchronized region
}
//上面的lock鎖等同于下面Monitor
Monitor.Enter(syscRoot);
try
{
   //synchronized region
}
finally
{
   Monitor.Exit(syscRoot);
}

Monitor 還可以設(shè)置超時(shí)時(shí)間,避免無(wú)限制的等待。同時(shí)它還有 Pulse\PulseAll\Wait 實(shí)現(xiàn)喚醒機(jī)制。

ReaderWriterLock#

很多時(shí)候,對(duì)資源的讀操作頻率要遠(yuǎn)遠(yuǎn)高于寫操作頻率,這種情況下,應(yīng)該對(duì)讀寫應(yīng)用不同的鎖,使得在沒有寫鎖時(shí),可以并發(fā)讀(加讀鎖),在沒有讀鎖或?qū)戞i時(shí),才可以寫(加寫鎖)。ReaderWriterLock就實(shí)現(xiàn)了此功能。

主要的特點(diǎn)是在沒有寫鎖時(shí),可以并發(fā)讀,而非一概而論,不論讀寫都只能一次一個(gè)線程。

MethodImpl(MethodImplOptions.Synchronized)#

如果是方法層面的線程同步,除上述的lock/Monitor之外,還可以使用MethodImpl(MethodImplOptions.Synchronized)特性修飾目標(biāo)方法。

SynchronizationAttribute#

ContextBoundObject#

要了解SynchronizationAttribute,不得不先說(shuō)說(shuō)ContextBoundObject

首先進(jìn)程中承載程序集運(yùn)行的邏輯分區(qū)我們稱之為AppDomain(應(yīng)用程序域),在應(yīng)用程序域中,存在一個(gè)或多個(gè)存儲(chǔ)對(duì)象的區(qū)域我們稱之為Context(上下文)

在上下文的接口當(dāng)中存在著一個(gè)消息接收器負(fù)責(zé)檢測(cè)攔截和處理信息。當(dāng)對(duì)象是MarshalByRefObject的子類的時(shí)候,CLR將會(huì)建立Transparent Proxy,實(shí)現(xiàn)對(duì)象與消息之間的轉(zhuǎn)換。應(yīng)用程序域是 CLR 中資源的邊界。一般情況下,應(yīng)用程序域中的對(duì)象不能被外界的對(duì)象所訪問,而MarshalByRefObject 的功能就是允許在支持遠(yuǎn)程處理的應(yīng)用程序中跨應(yīng)用程序域邊界訪問對(duì)象,在使用.NET Remoting遠(yuǎn)程對(duì)象開發(fā)時(shí)經(jīng)常使用到的一個(gè)父類。

ContextBoundObject更進(jìn)一步,它繼承 MarshalByRefObject,即使處在同一個(gè)應(yīng)用程序域內(nèi),如果兩個(gè) ContextBoundObject 所處的上下文不同,在訪問對(duì)方的方法時(shí),也會(huì)借由Transparent Proxy實(shí)現(xiàn),即采用基于消息的方法調(diào)用方式。這使得 ContextBoundObject 的邏輯永遠(yuǎn)在其所屬的上下文中執(zhí)行。

ps: 相對(duì)的,沒有繼承自 ContextBoundObjec t的類的實(shí)例則被視為上下文靈活的(context-agile),可存在于任意的上下文當(dāng)中。上下文靈活的對(duì)象總是在調(diào)用方的上下文中執(zhí)行。


一個(gè)進(jìn)程內(nèi)可以包括多個(gè)應(yīng)用程序域,也可以有多個(gè)線程。線程可以穿梭于多個(gè)應(yīng)用程序域當(dāng)中,但在同一個(gè)時(shí)刻,線程只會(huì)處于一個(gè)應(yīng)用程序域內(nèi)。線程也能穿梭于多個(gè)上下文當(dāng)中,進(jìn)行對(duì)象的調(diào)用。

SynchronizationAttribute用于修飾ContextBoundObject,使得其內(nèi)部構(gòu)成一個(gè)同步域,同一時(shí)段內(nèi)只允許一個(gè)線程進(jìn)入。


WaitHandle#

在查閱一些異步框架的源碼或接口時(shí),經(jīng)常能看到WaitHandle這個(gè)東西。WaitHandle 是一個(gè)抽象類,它有個(gè)核心方法WaitOne(int millisecondsTimeout, bool exitContext),第二個(gè)參數(shù)表示在等待前退出同步域。在大部分情況下這個(gè)參數(shù)是沒有用的,只有在使用SynchronizationAttribute修飾ContextBoundObject進(jìn)行同步的時(shí)候才有用。它使得當(dāng)前線程暫時(shí)退出同步域,以便其它線程進(jìn)入。具體請(qǐng)看本文 SynchronizationAttribute 小節(jié)。

WaitHandle 包含有以下幾個(gè)派生類:

  1. ManualResetEvent

  2. AutoResetEvent

  3. CountdownEvent

  4. Mutex

  5. Semaphore

ManualResetEvent#

可以阻塞一個(gè)或多個(gè)線程,直到收到一個(gè)信號(hào)告訴 ManualResetEvent 不要再阻塞當(dāng)前的線程。 注意所有等待的線程都會(huì)被喚醒。

可以想象 ManualResetEvent 這個(gè)對(duì)象內(nèi)部有一個(gè)信號(hào)狀態(tài)來(lái)控制是否要阻塞當(dāng)前線程,有信號(hào)不阻塞,無(wú)信號(hào)則阻塞。這個(gè)信號(hào)我們?cè)诔跏蓟臅r(shí)候可以設(shè)置它,如ManualResetEvent event=new ManualResetEvent(false);這就表明默認(rèn)的屬性是要阻塞當(dāng)前線程。

代碼舉例:

ManualResetEvent _manualResetEvent = new ManualResetEvent(false);

private void ThreadMainDo(object sender, RoutedEventArgs e)
{
   Thread t1 = new Thread(this.Thread1Foo);
   t1.Start(); //啟動(dòng)線程1
   Thread t2 = new Thread(this.Thread2Foo);
   t2.Start(); //啟動(dòng)線程2
   Thread.Sleep(3000); //睡眠當(dāng)前主線程,即調(diào)用ThreadMainDo的線程
   _manualResetEvent.Set();   //有信號(hào)
}

void Thread1Foo()
{
   //阻塞線程1
   _manualResetEvent.WaitOne();
   
   MessageBox.Show("t1 end");
}

void Thread2Foo()
{
   //阻塞線程2
   _manualResetEvent.WaitOne();
   
   MessageBox.Show("t2 end");
}

AutoResetEvent#

用法上和 ManualResetEvent 差不多,不再贅述,區(qū)別在于內(nèi)在邏輯。

與 ManualResetEvent 不同的是,當(dāng)某個(gè)線程調(diào)用Set方法時(shí),只有一個(gè)等待的線程會(huì)被喚醒,并被允許繼續(xù)執(zhí)行。如果有多個(gè)線程等待,那么只會(huì)隨機(jī)喚醒其中一個(gè),其它線程仍然處于等待狀態(tài)。

另一個(gè)不同點(diǎn),也是為什么取名Auto的原因:AutoResetEvent.WaitOne()會(huì)自動(dòng)將信號(hào)狀態(tài)設(shè)置為無(wú)信號(hào)。而一旦ManualResetEvent.Set()觸發(fā)信號(hào),那么任意線程再調(diào)用 ManualResetEvent.WaitOne() 就不會(huì)阻塞,除非在此之前先調(diào)用anualResetEvent.Reset()重置為無(wú)信號(hào)。

CountdownEvent#

它的信號(hào)有計(jì)數(shù)狀態(tài),可遞增AddCount()或遞減Signal(),當(dāng)?shù)竭_(dá)指定值時(shí),將會(huì)解除對(duì)其等待線程的鎖定。

注意:CountdownEvent 是用戶模式鎖。

Mutex#

Mutex 這個(gè)對(duì)象比較“專制”,同時(shí)段內(nèi)只能準(zhǔn)許一個(gè)線程工作。

Semaphore#

對(duì)比 Mutex 同時(shí)只有一個(gè)線程工作,Semaphore 可指定同時(shí)訪問某一資源或資源池的最大線程數(shù)。


輕量級(jí)同步#

.NET Framework 4 開始,System.Threading 命名空間中提供了六個(gè)新的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)允許細(xì)粒度的并發(fā)和并行化,并且降低一定必要的開銷,它們稱為輕量級(jí)同步原語(yǔ),它們都是用戶模式鎖,包括:

  • Barrier

  • CountdownEvent(上文已介紹)

  • ManualResetEventSlim (ManualResetEvent 的輕量替代,注意,它并不繼承 WaitHandle)

  • SemaphoreSlim (Semaphore 輕量替代)

  • SpinLock (可以認(rèn)為是 Monitor 的輕量替代)

  • SpinWait

Barrier#

當(dāng)在需要一組任務(wù)并行地運(yùn)行一連串的階段,但是每一個(gè)階段都要等待其他任務(wù)完成前一階段之后才能開始時(shí),您可以通過使用Barrier類的實(shí)例來(lái)同步這一類協(xié)同工作。當(dāng)然,我們現(xiàn)在也可以使用異步Task方式更直觀地完成此類工作。

SpinWait#

如果等待某個(gè)條件滿足需要的時(shí)間很短,而且不希望發(fā)生昂貴的上下文切換,那么基于自旋的等待時(shí)一種很好的替換方案。SpinWait不僅提供了基本自旋功能,而且還提供了SpinWait.SpinUntil方法,使用這個(gè)方法能夠自旋直到滿足某個(gè)條件為止。此外 SpinWait 是一個(gè)Struct,從內(nèi)存的角度上說(shuō),開銷很小。

需要注意的是:長(zhǎng)時(shí)間的自旋不是很好的做法,因?yàn)樽孕龝?huì)阻塞更高級(jí)的線程及其相關(guān)的任務(wù),還會(huì)阻塞垃圾回收機(jī)制。SpinWait 并沒有設(shè)計(jì)為讓多個(gè)任務(wù)或線程并發(fā)使用,因此需要的話,每一個(gè)任務(wù)或線程都應(yīng)該使用自己的 SpinWait 實(shí)例。

當(dāng)一個(gè)線程自旋時(shí),會(huì)將一個(gè)內(nèi)核放入到一個(gè)繁忙的循環(huán)中,而不會(huì)讓出當(dāng)前處理器時(shí)間片剩余部分,當(dāng)一個(gè)任務(wù)或者線程調(diào)用Thread.Sleep方法時(shí),底層線程可能會(huì)讓出當(dāng)前處理器時(shí)間片的剩余部分,這是一個(gè)大開銷的操作。

因此,在大部分情況下, 不要在循環(huán)內(nèi)調(diào)用 Thread.Sleep 方法等待特定的條件滿足 。

SpinLock是對(duì) SpinWait 的簡(jiǎn)單封裝。

轉(zhuǎn)自https://www.cnblogs.com/newton/p/18365359


該文章在 2024/8/19 8:31:40 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved

主站蜘蛛池模板: 国产福利一区二区三区在线观看 | 一区二区三区免费播放 | 免费视频网站 | 亚洲香蕉 | 精品国产午夜福利在线观看蜜月 | 免费国产亚洲精品在线视频 | 欲香欲色天 | 字幕一区在线观看视频 | 全黄性性激高免费视频 | 草莓视频在线免费看 | 国产精品三级在线观看 | 亚洲欧美日韩在线综合网 | 97porm国内自拍视频 | 91视频成人 | 精品国产亚洲一区二区三区在线观 | 亚洲综合精品网站在线观看 | 国产欧美日韩视频专区在线观看 | 国产偷国产偷精 | 在线播放精品一区二区三区 | 国产高清无密码一区二区三区 | 国产啪精品视频网站 | 亚洲一区二区三区乱 | 亚洲日韩高清在线亚洲专区 | 欧美精品一区二区三区 | 欧美乱妇高清免费96欧美乱妇高 | 国产自国产自愉自愉免费24区 | 国产日产精品 | 九三精品私密视频在线观看 | 国产一区二区三区精品视 | 欧美日韩国产精品一区二区 | 国产微信高清小视频在线播放 | 精品亚洲一区 | 欧美日产欧美日产精品 | 国内成人福利短视频在线 | 91精品乱码一区二区三区 | 中文字幕日韩经典 | 视频一二三区 | 亚洲中文在线不卡 | 亚洲a级情欲片在线观看 | h片在线观看免 | 欧美色欧美亚洲高清在线观看 |