原文 永遠不要使用
__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); }
//編譯,正确.