Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/297.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Volatile字段:如何实际获取字段的最新写入值?_C#_.net_Multithreading_.net 4.5_Volatile - Fatal编程技术网

C# Volatile字段:如何实际获取字段的最新写入值?

C# Volatile字段:如何实际获取字段的最新写入值?,c#,.net,multithreading,.net-4.5,volatile,C#,.net,Multithreading,.net 4.5,Volatile,考虑以下例子: private int sharedState = 0; private void FirstThread() { Volatile.Write(ref sharedState, 1); } private void SecondThread() { int sharedStateSnapshot = Volatile.Read(ref sharedState); Console.WriteLine(sharedStateSnapshot); } 直

考虑以下例子:

private int sharedState = 0;

private void FirstThread() {
    Volatile.Write(ref sharedState, 1);
}

private void SecondThread() {
    int sharedStateSnapshot = Volatile.Read(ref sharedState);
    Console.WriteLine(sharedStateSnapshot);
}
直到最近,我的印象是,只要
FirstThread()
确实在
SecondThread()之前执行,这个程序就只能输出1

然而,我现在的理解是:

  • Volatile.Write()发出释放栅栏。这意味着在将1分配给
    sharedState
    之后,不会发生先前的加载或存储(按程序顺序)
  • Volatile.Read()发出一个获取围栏。这意味着在将
    sharedState
    复制到
    sharedStateSnapshot
    之前,不会发生后续加载或存储(按程序顺序)
或者,换一种说法:

  • sharedState
    实际发布到所有处理器内核时,写入之前的所有内容也将被发布,并且
  • 获取地址
    sharedStateSnapshot
    中的值时<代码>共享数据状态
必须已获取 因此,如果我的理解是正确的,那么如果写入的
FirstThread()
尚未释放,那么就没有任何东西可以阻止获取
sharedState
变得“过时”


如果这是真的,我们如何实际确保(假设最脆弱的处理器内存模型,如ARM或Alpha)程序始终打印1?(或者我的心智模型在什么地方出错了?

你是对的,不能保证所有处理器都能立即看到发布存储<代码>易失性。读和
易失性。写
提供获取/释放语义,但没有即时性保证

不过,
volatile
修饰符似乎可以做到这一点。编译器将发出一条
操作码.Volatile
IL指令,抖动将告诉处理器不要将变量存储在其任何寄存器上(请参阅)


但是,你为什么需要它是立即的呢?如果您的
SecondThread
在实际写入值之前提前几毫秒运行,该怎么办?由于日程安排是不确定的,您的程序的正确性无论如何不应该依赖于这种“即时性”。

您的理解是正确的,您确实无法确保程序始终使用这些技术打印1。为了确保程序将打印1,假设线程2在线程1之后运行,每个线程上需要两个围栏

实现这一点的最简单方法是使用
lock
关键字:

private int sharedState = 0;
private readonly object locker = new object();

private void FirstThread() 
{
    lock (locker)
    {
        sharedState = 1;
    }
}

private void SecondThread() 
{
    int sharedStateSnapshot;
    lock (locker)
    {
        sharedStateSnapshot = sharedState;
    }
    Console.WriteLine(sharedStateSnapshot);
}
我想引用:

坦率地说,我不鼓励你做一个不稳定的领域。易变字段表明您正在做一些完全疯狂的事情:您试图在两个不同的线程上读取和写入相同的值,而没有设置锁

这同样适用于调用
Volatile.Read
Volatile.Write
。事实上,它们甚至比volatile字段更糟糕,因为它们需要您手动执行
volatile
修饰符自动执行的操作

直到最近,我的印象是,只要 FirstThread()确实是在SecondThread()之前执行的,这个程序 无法输出除1以外的任何内容

当你继续解释自己时,这种印象是错误的
Volatile.Read
只是在其目标上发出一个读取操作,后跟一个内存屏障;内存屏障阻止在执行当前线程的处理器上重新排序操作,但这在这里没有帮助,因为

  • 没有要重新排序的操作(只需在每个线程中执行单个读取或写入操作)
  • 线程之间的竞争条件意味着,即使在处理器之间应用了无重新排序保证,也只意味着无法预测的操作顺序将被保留
  • 因此,如果我的理解是正确的,那么就没有什么可担心的了 如果写入失败,则防止获取sharedState“过时” FirstThread()尚未发布

    这是正确的。本质上,您正在使用一种工具,该工具旨在帮助处理弱内存模型,以应对竞争条件可能导致的问题。这个工具帮不了你,因为它不是这样做的

    如果这是真的,我们如何才能真正确保(假设最弱的 处理器内存模型,如ARM或Alpha),程序将 总是打印1?(或者我的思维模式有错误吗 在什么地方?)

    再次强调:内存模型不是这里的问题。为确保您的程序始终打印1,您需要做两件事:

  • 提供明确的线程同步,以确保写入将在读取之前发生(在最简单的情况下,
    SecondThread
    可以在标志上使用旋转锁,
    FirstThread
    使用该标志来表示写入完成)
  • 确保
    SecondThread
    不会读取过时的值。通过将
    sharedState
    标记为
    volatile
    ,您可以轻松地做到这一点——虽然这个关键字理所当然地受到了很多抨击,但它是专门为此类用例设计的
  • 例如,在最简单的情况下,您可以:

    private volatile int sharedState = 0;
    private volatile bool spinLock = false;
    
    private void FirstThread()
    {
        sharedState = 1;
        // ensure lock is released after the shared state write!
        Volatile.Write(ref spinLock, true); 
    }
    
    private void SecondThread()
    {
        SpinWait.SpinUntil(() => spinLock);
        Console.WriteLine(sharedState);
    }
    

    假设没有其他写入到这两个字段,则该程序保证只输出1。

    这取决于您如何知道
    FirstThread()
    SecondThread()之前运行。这一知识是否也基于其他代码和内存模型,或者这正是程序员认为的情况?@Damien_不相信者我不确定我是否理解你的问题……你说“
    FirstThread()
    确实在
    SecondThread()
    之前执行过”—但你怎么知道?您是否有其他一些代码没有向我们展示,它们在某种程度上也依赖于内存模型,能够检测到
    FirstThread
    具有def