天天看点

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); }
//编译,正确.      

继续阅读