麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發設計 > 正文

細說.NET中的多線程(六使用MemoryBarrier,Volatile進行同步)

2019-11-14 13:49:22
字體:
來源:轉載
供稿:網友

上一節介紹了使用信號量進行同步,本節主要介紹一些非阻塞同步的方法。本節主要介紹MemoryBarrier,volatile,Interlocked。

MemoryBarriers

本文簡單的介紹一下這兩個概念,假設下面的代碼:

using System;class Foo{    int _answer;    bool _complete;    void A()    {        _answer = 123;        _complete = true;    }    void B()    {        if (_complete) Console.WriteLine(_answer);    }}

如果方法A和方法B同時在兩個不同線程中運行,控制臺可能輸出0嗎?答案是可能的,有以下兩個原因:

  • 編譯器,CLR或者CPU可能會更改指令的順序來提高性能
  • 編譯器,CLR或者CPU可能會通過緩存來優化變量,這種情況下對其他線程是不可見的。

最簡單的方式就是通過MemoryBarrier來保護變量,來防止任何形式的更改指令順序或者緩存。調用Thread.MemoryBarrier會生成一個內存柵欄,我們可以通過以下的方式解決上面的問題:

using System;using System.Threading;class Foo{    int _answer;    bool _complete;    void A()    {        _answer = 123;        Thread.MemoryBarrier();    // Barrier 1        _complete = true;        Thread.MemoryBarrier();    // Barrier 2    }    void B()    {        Thread.MemoryBarrier();    // Barrier 3        if (_complete)        {            Thread.MemoryBarrier();       // Barrier 4            Console.WriteLine(_answer);        }    }}

上面的例子中,barrier1和barrier3用來保證指令順序不會改變,barrier2和barrier4用來保證值變化不被緩存。一個好的處理方案就是我們在需要保護的變量前后分別加上MemoryBarrier。

在c#中,下面的操作都會生成MemoryBarrier:

  • Lock語句(Monitor.Enter,Monitor.Exit)
  • 所有Interlocked類的方法
  • 線程池的回調方法
  • Set或者Wait信號
  • 所有依賴于信號燈實現的方法,如starting或waiting 一個Task

因為上面這些行為,這段代碼實際上是線程安全的:

        int x = 0;        Task t = Task.Factory.StartNew(() => x++);        t.Wait();        Console.WriteLine(x);    // 1

在你自己的程序中,你可能重現不出來上面例子所說的情況。事實上,從msdn上對MomoryBarrier的解釋來看,只有對順序保護比較弱的多核系統才需要用到MomoryBarrier。但是有一點需要注意:多線程去修改變量并且不使用任何形似的鎖或者內存柵欄是會帶來一定的麻煩的。

下面一個例子能夠很好的說明上面的觀點(在你的VisualStudio中,選擇Release模式,并且Start Without Debugging重現這個問題):

        bool complete = false;        var t = new Thread(() =>        {            bool toggle = false;            while (!complete) toggle = !toggle;        });        t.Start();        Thread.Sleep(1000);        complete = true;        t.Join();        // Blocks indefinitely

這個程序永遠不會結束,因為complete變量被緩存在了CPU寄存器中。在while循環中加入Thread.MemoryBarrier可以解決這個問題。

volatile關鍵字

另外一種更高級的方式來解決上面的問題,那就是考慮使用volatile關鍵字。Volatile關鍵字告訴編譯器在每一次讀操作時生成一個fence,來實現保護保護變量的目的。具體說明可以參見msdn的介紹

VolatileRead和VolatileWrite

Volatile關鍵字只能加到類變量中。本地變量不能被聲明成volatile。這種情況你可以考慮使用System.Threading.Volatile.Read方法。我們看一下System.Threading.Volatile源碼如何實現這兩個方法的:

    public static bool Read(ref bool location)    {        bool flag = location;        Thread.MemoryBarrier();        return flag;    }    public static void Write(ref bool location, bool value)    {        Thread.MemoryBarrier();        location = value;    }

  

一目了然,通過MemoryBarrier來實現的,但是他只在讀操作的后面和寫操作的前面加了MemoryBarrier,那么你應該考慮,如果你先使用Volatile.Write再使用Volatile.Read是不是可能有問題呢?

c#中ConcurrentDictionary中使用了Volatile類來保護變量,有興趣的讀者可以看看c#的開發者是如何使用這個方法來保護變量的。

Interlocked

使用MemoryBarrier并不總是一個好的解決方案,尤其在不需要鎖的情況下。Interlocked方法提供了一些常用的原子操作來避免前面文章提到的一系列的問題。如使用Interlocked.Increment來替代++,Interlocked.Decrement來替代--。Msdn的文檔中詳細的介紹了相關的用法和原理。C#中的源碼里也經常能看見Interlocked相關的使用。

 

本文介紹了一些除了鎖和信號量之外的一些同步方式,歡迎批評與指正。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 久久久大片 | 91性视频 | 中国av免费在线观看 | 国产精品一区二区x88av | 国产一区二区三区精品在线观看 | 手机av免费电影 | 久久精品亚洲精品国产欧美kt∨ | 国产美女视频黄a视频免费 日韩黄色在线播放 | 精品久久久久久久 | 午夜精品久久久久久久96蜜桃 | 成人午夜激情网 | 欧美人与禽性xxxxx杂性 | 精品一区二区亚洲 | 末成年女av片一区二区 | 日本精品免费观看 | 国产精品美女一区二区 | 国产超碰人人爽人人做人人爱 | 大学生a级毛片免费视频 | 国产一区二区三区视频在线 | 一级毛片免费观看在线 | 视频一区二区不卡 | 国产激情视频在线 | 一级做a爱性色毛片免费1 | 99精品视频在线看 | 天天夜夜操操 | 欧美日韩国产一区二区三区在线观看 | 国产一区二区三区四区五区精品 | 成人在线视频播放 | 久久精品国产清自在天天线 | 久久久一区二区三区精品 | 高清中文字幕在线 | 免费小毛片 | 五月天影院,久久综合, | 精品久久久久久久久久久久包黑料 | 一本色道久久99精品综合蜜臀 | 成人18网站 | 日本在线播放一区 | 午夜久久久久 | www.国产一区.com | 请播放一级毛片 | 日韩av在线网址 |