c#变量新鲜度

本文关键字:新鲜度 变量 | 更新日期: 2023-09-27 17:53:40

假设类中有一个成员变量(具有原子读/写数据类型):

bool m_Done = false;

然后创建一个任务将其设置为true:

Task.Run(() => m_Done = true);

我不关心m_Done何时被设置为true。我的问题是,我是否有c#语言规范和任务并行库的保证最终m_Done将是真的,如果我从不同的线程访问它?
例子:

if(m_Done) { // Do something }

我知道使用锁会引入必要的内存屏障,m_Done稍后会显示为true。我也可以使用Volatile。设置变量和Volatile时写入。阅读的时候阅读它。我看到很多这样写的代码(没有锁或volatile),我不确定它是否正确。

请注意,我的问题不是针对c#或。net的特定实现,它的目标是规范。我需要知道当前代码是否会在x86, x64, Itanium或ARM上运行。

c#变量新鲜度

我不关心m_Done何时被设置为true。我的问题是,我是否有c#语言规范和任务并行库的保证,最终m_Done将是真的,如果我从不同的线程访问它?

m_Done的读是非易失性的,因此可以在时间上任意向后移动,并且结果可能被缓存。因此,可以观察到每次读取时都为false

我需要知道当前代码是否会在x86, x64, Itanium或ARM上运行。

规范不能保证代码在强(x86)和弱(ARM)内存模型上执行相同的操作。

规范对非易失性读写的保证非常清楚:在没有某些特殊事件(如锁)的情况下,它们可以在不同的线程上任意重新排序。

阅读规范以了解详细信息,特别是关于与volatile访问相关的副作用的部分。如果你有更多的问题,然后发布一个新的问题。这是非常棘手的事情。

此外,该问题假定您忽略了确定任务是否完成的现有机制,而是使用自己的机制。现有机制由专家设计;使用它们。

我看到很多这样写的代码(没有锁或volatile),我不确定它是否正确。

几乎可以肯定不是。

对于编写代码的人来说,一个很好的练习是:

static volatile bool q = false;
static volatile bool r = false;
static volatile bool s = false;
static volatile bool t = false;
static object locker = new object();
static bool GetR() { return r; }  // No lock!
static void SetR() { lock(locker) { r = true; } }
static void MethodOne()
{
  q = true;
  if (!GetR())
    s = true;
}
static void MethodTwo()
{
  SetR();
  if (!q)
    t = true;
}

初始化字段后,从一个线程调用MethodOne,从另一个线程调用MethodTwo。注意,一切都是易失性的,对r的写入不仅是易失性的,而且是完全隔离的。这两种方法都正常完成。是否有可能在第一个线程上s和t都被观察到为真?在x86上可能吗?看来并非如此;如果第一个线程赢了,那么t仍然为假,如果第二个线程赢了,那么s仍然为假;这种分析是错误的。为什么?(提示:x86如何允许重写MethodOne ?)

如果程序员无法回答这个问题,那么他们几乎肯定无法正确地使用volatile进行编程,并且不应该在没有锁的情况下跨线程共享内存。

试试这段代码,构建发布版,不使用Visual Studio运行:

class Foo
{
    private bool m_Done = false;
    public void A()
    {
        Task.Run(() => { m_Done = true; });
    }
    public void B()
    {
        for (; ; )
        {
            if (m_Done)
                break;
        }
        Console.WriteLine("finished...");
    }
}
class Program
{
    static void Main(string[] args)
    {
        var o = new Foo();
        o.A();
        o.B();
        Console.ReadKey();
    }
}

你有很好的机会看到它永远运行