.NET中各種線程同步鎖
當(dāng)前位置:點(diǎn)晴教程→知識管理交流
→『 技術(shù)文檔交流 』
編程編的久了,總會遇到多線程的情況,有些時(shí)候我們要幾個線程合作完成某些功能,這時(shí)候可以定義一個全局對象,各個線程根據(jù)這個對象的狀態(tài)來協(xié)同工作,這就是基本的 支持多線程編程的語言一般都內(nèi)置了一些類型和方法用于創(chuàng)建上述所說的全局對象也就是 ps:本文雖然關(guān)注 .Net 平臺,但涉及到的大部分鎖概念都是平臺無關(guān)的,在很多其它語言(如_ volatile 關(guān)鍵字#確切地說, 緩存一致性#了解 我們知道,在現(xiàn)代計(jì)算機(jī)中,處理器的指令速度遠(yuǎn)超內(nèi)存的存取速度,所以現(xiàn)代計(jì)算機(jī)系統(tǒng)都不得不加入一層讀寫速度盡可能接近處理器運(yùn)算速度的高速緩存來作為主存與處理器之間的緩沖。處理器計(jì)算直接存取的是高速緩存中的數(shù)據(jù),計(jì)算完畢后再同步到主存中。 在多處理器系統(tǒng)中,每個處理器都有自己的高速緩存,而它們又共享同一主存。 而 Java 內(nèi)存模型的每個線程有自己的工作內(nèi)存,其中保留了被線程使用的變量的副本。線程對變量的所有的操作都必須在工作內(nèi)存中完成,而不能直接讀寫主內(nèi)存中的變量。不同線程之間也不能直接訪問對方工作內(nèi)存中的變量,線程間變量的值的傳遞需要通過主內(nèi)存中轉(zhuǎn)來完成。 雖然兩者的設(shè)計(jì)相似,但是前者主要解決存取效率不匹配的問題,而后者主要解決內(nèi)存安全(競爭、泄露)方面的問題。顯而易見,這種設(shè)計(jì)方案引入了新的問題—— 為了解決這個問題,很多平臺都內(nèi)置了 volatile 關(guān)鍵字,使用它修飾的變量,可以保證所有線程每次獲取到的是最新值。這是怎么做到的呢?這就要求所有線程在訪問變量時(shí)遵循預(yù)定的協(xié)議,比如 另外 volatile 還能避免編譯器自作聰明重排指令。重排指令在大多數(shù)時(shí)候無傷大雅,還能對執(zhí)行效率有一定提升,但某些時(shí)候會影響到執(zhí)行結(jié)果,此時(shí)就可以使用 volatile。 Interlocked#同 volatile 的 它的原子操作基于 CPU 本身,非阻塞,所以也不是真正意義上的鎖,當(dāng)然效率會比鎖高得多。 鎖模式#接下來正式介紹各種鎖之前,先了解下鎖模式——鎖分為 內(nèi)核模式就是在系統(tǒng)級別讓線程中斷,收到信號時(shí)再切回來繼續(xù)干活。該模式在線程掛起時(shí)由系統(tǒng)底層負(fù)責(zé),幾乎不占用 CPU 資源,但線程切換時(shí)效率低。 用戶模式就是通過一些 CPU 指令或者死循環(huán)讓線程一直運(yùn)行著直到可用。該模式下,線程掛起會一直占用 CPU 資源,但線程切換非常快。 長時(shí)間的鎖定,優(yōu)先使用內(nèi)核模式鎖;如果有大量的鎖定,且鎖定時(shí)間非常短,切換頻繁,用戶模式鎖就很有用。另外內(nèi)核模式鎖可以實(shí)現(xiàn)跨進(jìn)程同步,而用戶模式鎖只能進(jìn)程內(nèi)同步。 本文中,除文末 lock 關(guān)鍵字#
Monitor#上面 lock 就是
Monitor 還可以設(shè)置超時(shí)時(shí)間,避免無限制的等待。同時(shí)它還有 ReaderWriterLock#很多時(shí)候,對資源的讀操作頻率要遠(yuǎn)遠(yuǎn)高于寫操作頻率,這種情況下,應(yīng)該對讀寫應(yīng)用不同的鎖,使得在沒有寫鎖時(shí),可以并發(fā)讀(加讀鎖),在沒有讀鎖或?qū)戞i時(shí),才可以寫(加寫鎖)。 主要的特點(diǎn)是在沒有寫鎖時(shí),可以并發(fā)讀,而非一概而論,不論讀寫都只能一次一個線程。 MethodImpl(MethodImplOptions.Synchronized)#如果是方法層面的線程同步,除上述的 SynchronizationAttribute#ContextBoundObject#要了解 首先進(jìn)程中承載程序集運(yùn)行的邏輯分區(qū)我們稱之為 在上下文的接口當(dāng)中存在著一個消息接收器負(fù)責(zé)檢測攔截和處理信息。當(dāng)對象是 而 ps: 相對的,沒有繼承自 ContextBoundObjec t的類的實(shí)例則被視為 一個進(jìn)程內(nèi)可以包括多個應(yīng)用程序域,也可以有多個線程。線程可以穿梭于多個應(yīng)用程序域當(dāng)中,但在同一個時(shí)刻,線程只會處于一個應(yīng)用程序域內(nèi)。線程也能穿梭于多個上下文當(dāng)中,進(jìn)行對象的調(diào)用。
WaitHandle#在查閱一些異步框架的源碼或接口時(shí),經(jīng)常能看到 WaitHandle 包含有以下幾個派生類:
ManualResetEvent#可以阻塞一個或多個線程,直到收到一個信號告訴 ManualResetEvent 不要再阻塞當(dāng)前的線程。 注意所有等待的線程都會被喚醒。 可以想象 ManualResetEvent 這個對象內(nèi)部有一個信號狀態(tài)來控制是否要阻塞當(dāng)前線程,有信號不阻塞,無信號則阻塞。這個信號我們在初始化的時(shí)候可以設(shè)置它,如 代碼舉例:
AutoResetEvent#用法上和 ManualResetEvent 差不多,不再贅述,區(qū)別在于內(nèi)在邏輯。 與 ManualResetEvent 不同的是,當(dāng)某個線程調(diào)用Set方法時(shí),只有一個等待的線程會被喚醒,并被允許繼續(xù)執(zhí)行。如果有多個線程等待,那么只會隨機(jī)喚醒其中一個,其它線程仍然處于等待狀態(tài)。 另一個不同點(diǎn),也是為什么取名 CountdownEvent#它的信號有計(jì)數(shù)狀態(tài),可遞增 注意:CountdownEvent 是用戶模式鎖。 Mutex#Mutex 這個對象比較“專制”,同時(shí)段內(nèi)只能準(zhǔn)許一個線程工作。 Semaphore#對比 Mutex 同時(shí)只有一個線程工作, 輕量級同步#.NET Framework 4 開始,System.Threading 命名空間中提供了六個新的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)允許細(xì)粒度的并發(fā)和并行化,并且降低一定必要的開銷,它們稱為輕量級同步原語,它們都是用戶模式鎖,包括:
Barrier#當(dāng)在需要一組任務(wù)并行地運(yùn)行一連串的階段,但是每一個階段都要等待其他任務(wù)完成前一階段之后才能開始時(shí),您可以通過使用 SpinWait#如果等待某個條件滿足需要的時(shí)間很短,而且不希望發(fā)生昂貴的上下文切換,那么基于自旋的等待時(shí)一種很好的替換方案。 需要注意的是:長時(shí)間的自旋不是很好的做法,因?yàn)樽孕龝枞呒壍木€程及其相關(guān)的任務(wù),還會阻塞垃圾回收機(jī)制。SpinWait 并沒有設(shè)計(jì)為讓多個任務(wù)或線程并發(fā)使用,因此需要的話,每一個任務(wù)或線程都應(yīng)該使用自己的 SpinWait 實(shí)例。 當(dāng)一個線程自旋時(shí),會將一個內(nèi)核放入到一個繁忙的循環(huán)中,而不會讓出當(dāng)前處理器時(shí)間片剩余部分,當(dāng)一個任務(wù)或者線程調(diào)用 因此,在大部分情況下, 不要在循環(huán)內(nèi)調(diào)用 Thread.Sleep 方法等待特定的條件滿足 。
轉(zhuǎn)自https://www.cnblogs.com/newton/p/18365359 該文章在 2024/8/19 8:31:40 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |