天天看點

d中shared用法

原文​ 永遠不要使用​

​__gshared​

​.這是明顯安全漏洞.改為使用​

​shared​

​.

如果使用時遇見​

​shared​

​的​

​編譯​

​錯誤,那是​

​編譯器​

​在警告你.應該認真​

​考慮​

​線程安全,然後才在正确的位置抛棄​

​shared​

​.

使用​

​__gshared​

​,編譯器假裝沒有看到​

​變量​

​是​

​共享​

​的.并除非仔細思考,保證會生成​

​競争​

​.

順便,有優先​

​__gshared​

​​而非​

​shared​

​​的情況嗎?似乎許多新手在用​

​__gshared​

​.

我​

​不完全​

​​同意該點,這僅取決于你用法.一般,你應該使用​

​shared​

​​,但​

​__gshared​

​​有意義.隻是在​

​多線程​

​​可更改時才有問題,但如果它僅從​

​單線程​

​​更改但從​

​多線程​

​​讀取,則一般不是問題.

總之:

單寫/單讀?用​​

​__gshared​

​​ 單寫/多讀?用​

​__gshared​

​ 多寫/單讀?用​

​shared​

​ 多寫/多讀?用​

​shared​

如果與C對接,則需要​

​__gshared​

​​.但是,是的,這裡應該使用​

​shared​

​.

​__gshared​

​​表現不錯,但我優先用​

​std.concurrency​

​,示例:

import std.stdio;
import std.concurrency;
import core.thread;

struct Result {
  int value;
}

struct Done {
}

void run()
{
  bool done = false;
  while (!done) {
    writeln("運作子線程");
    receiveTimeout(1.seconds,
        (Done msg) {
            done = true;
        });
  }

  // `發送`結果給所有者
  // 假定,線程在上面循環中産生結果
  ownerTid.send(Result(42));
}

void main()
{
    auto worker = spawn(&run);
    Thread.sleep(5.seconds);
    worker.send(Done());
    auto result = receiveOnly!Result();
    writeln("結果:", result);
}      

所有​

​這些​

​​都可能是​

​競争條件​

​​.

這是​​

​單寫單讀​

​的:

align(64) static struct S
{
    align(1):
    ubyte[60] off;
    ulong x = 0;
}
__gshared S s;
void main()
{
    import core.thread:;
    import std.conv:;
    new Thread(() {
        foreach (i; 0 .. uint.max)
        {
            s.x = 0;
            s.x = -1;
        }
    }).start();
    foreach (i; 0 .. uint.max)
    {
        auto x = s.x;
        assert(x == 0 || x == -1, to!string(x, 16));
    }
}      

如果你知道如何​

​安全​

​​地通路​

​變量​

​​,則可用​

​shared​

​​.我堅持:永遠不要使用​

​__gshared​

​​.

快速測試表明​​

​extern(C) extern shared​

​​工作很好.

據我所知,​​

​__gshared​

​​僅在,你想在​

​單線程​

​​程式中通路​

​共享C變量​

​​時才工作良好.然後,如果稍後用​

​多線程​

​​,你仍然會​

​失敗​

​​.

是以,永遠不要(在​​

​多線程​

​​代碼中)使用​

​__gshared​

​.

C沒有​

​共享​

​​概念,是以不是正确​

​類型​

​​.加上​

​shared​

​​隻是​

​假​

​​,并會導緻麻煩.最好​

​明确​

​​說明.

并不是說你應該自由使用​​

​__gshared​

​​,或隻在​

​D中​

​​使用.而是說​

​永遠不應​

​使用它,這是錯誤的.

廢話.在共享變量上加​

​shared​

​​不是"說謊".​

​C​

​​是否區分并不重要.但​

​D​

​重要.

如果你可識别出​

​__gshared​

​​的​

​有效用例​

​​,并用它​

​編寫​

​​正确代碼,則你就知道​

​什麼​

​​時候不聽我的.

其他人,​​

​永遠​

​​不要使用​

​__gshared​

​​.

​​

​__gshared​

​​和​

​-boundscheck=off​

​​一樣糟糕.它們都是明顯的​

​安全漏洞​

​.

auto x=s.x;      

你的問題在此,而不是因為它是​

​__gshared​

​​.

你複制了該值,顯然可同時更改它,這是常識.

你不應這樣使用它.而應該直接通路​​

​s.x​

​​.

而用​​

​共享​

​​,如果​

​讀取線程​

​​時先鎖定,則結果相同,且在​

​更改前​

​會讀取并處理該值.

讀​

​x​

​​時,就改了​

​x​

​.

auto x = s.x;
assert(s.x == 0 || s.x == -1, to!string(s.x, 16));
//多個競争替代原來的1個競争,且不能定位問題      

​shared​

​​并不能解決條件競争,這是對的.如果沒有​

​-preview=nosharedaccess​

​​,則無差別.是以不妨使用​

​shared​

​​😉.

但是有了​​

​-preview=nosharedaccess​

​​,代碼不再編譯,你​

​不得不​

​​考慮如何​

​安全​

​​通路​

​共享資料​

​​.哪個好?

是以:永遠不要使用​​

​__gshared​

​​,總是用​

​-preview=nosharedaccess​

​.

如果你有​

​更好​

​​抽象且仔細​

​鎖定​

​​通路資料,但​

​C不行​

​​,如果想通路​

​C全局變量​

​​,應使用​

​__gshared​

​​,因為這就是它的​

​用途​

​​.使用​

​shared​

​​,幫不了你.

使用​​

​__gshared​

​​來共享資料​

​給C​

​​,與使用​

​-boundscheck=on​

​​發送數組到無此類限制的​

​C中​

​​一樣安全.

這裡​​

​結論​

​​真的應該是,不要使用​

​C​

​.

不,使用​

​shared​

​​确實可以幫你.

C沒有​​

​shared​

​​限定符,但​

​C程式員​

​​仍然必須考慮​

​線程安全​

​​.調用​

​C函數​

​​或通路​

​C全局變量​

​​必須考慮​

​多線程​

​​.​

​shared加上(-preview=nosharedaccess)​

​​迫使你考慮​

​合同​

​​是什麼.​

​__gshared​

​沒有.

不,這不對.​

​C​

​​總是​

​不安全​

​​的,這是正确的,但不重要.關鍵是你在​

​D端​

​​可以/不能​

​做什麼​

​.

​-boundscheck=on​

​​,不會輕易在​

​D端​

​​搞砸.​

​C​

​​端還是可以亂七八糟的.

​​

​-boundscheck=off​

​​,​

​很容易​

​​在D端搞砸.

​​

​shared​

​​,不會輕易在D端搞砸.​

​C​

​​端還是可以亂七八糟的.

​​

​__gshared​

​​,很容易在​

​D端​

​搞砸.

​shared​

​​讓你感覺​

​該語言​

​​幫你預防問題.同樣,對C,這是​

​假的​

​.

​邊界​

​​在C和D中的定義相同,有​

​指針​

​​和​

​大小​

​​,你不能超過那個大小.是的,資料以不同的方式傳達,但這很容易了解和使用.

​​

​shared​

​​(加上​

​-preview=nosharedaccess​

​​),​

​阻止​

​​你上場.不會犯規.不能傷害​

​自己​

​​.可通過​

​強制轉換​

​​告訴編譯器​

​(1)​

​​你确定要玩,及​

​(2)​

​​,你将按C端的規則玩.

​​

​__gshared​

​​隻是讓你在​

​球場​

​​上奔跑.不知道規矩?編譯器不在乎.​

​玩得開心​

​​打斷你的腿.

用​​

​__gshared​

​:

extern(C) extern __gshared int x;
void fun() { x = 42; }
//編譯,條件競争      

我​

​甚至​

​​未發現我在做​

​危險​

​​事情,因為第一次​

​天真​

​​的通過了編譯且似乎​

​工作正常​

​​.

使用​​

​shared​

​​(加​

​-preview=nosharedaccess​

​):

extern(C) extern shared int x;
void fun() { x = 42; } /* 錯誤 */      

如果檢視文檔,會發現關于​

​正确用法​

​.正如你所建議的,我想出了:

extern(C) extern shared int x;
void fun() { properlyUse(&x, 42); }
//仍錯誤,但共享.      

我被迫​

​更多​

​​地考慮​

​線程安全​

​​.這裡可以丢棄​

​shared​

​​,因為調用了線程安全的​

​properlyUse​

​函數.是以:

extern(C) extern shared int x;
void fun() { properlyUse(cast(int*) &x, 42); }
//編譯,正确.      

繼續閱讀