Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/326.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#可变新鲜度_C#_Multithreading_Task Parallel Library_Volatile_Memory Model - Fatal编程技术网

C#可变新鲜度

C#可变新鲜度,c#,multithreading,task-parallel-library,volatile,memory-model,C#,Multithreading,Task Parallel Library,Volatile,Memory Model,假设类中有一个成员变量(具有原子读/写数据类型): 然后我创建一个任务将其设置为true: Task.Run(() => m_Done = true); 我不在乎什么时候m_Done会被设置为true。 我的问题是,我是否得到了C语言规范和任务并行库的保证 如果我从另一个线程访问它,m_Done最终将为真? 例如: 我知道使用锁会引入必要的内存障碍,而m_Done稍后将显示为true。 我也可以使用Volatile。设置变量时写入,设置变量时读取 读它。 我看到很多这样编写的代码(没有锁

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

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

Task.Run(() => m_Done = true);
我不在乎什么时候m_Done会被设置为true。 我的问题是,我是否得到了C语言规范和任务并行库的保证 如果我从另一个线程访问它,m_Done最终将为真?
例如:

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

请注意,我的问题不是针对C#或.Net的具体实现, 它的目标是规范。 我需要知道如果在x86、x64、安腾或ARM上运行,当前代码的行为是否类似

我不在乎什么时候m_Done会被设置为true。我的问题是,如果我从另一个线程访问它,我是否有C语言规范和任务并行库的保证,最终m#u Done将是真的

没有

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

我需要知道如果在x86、x64、安腾或ARM上运行,当前代码的行为是否类似

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

该规范非常清楚地说明了如何保证非易失性读写:在没有锁等特定事件的情况下,它们可以在不同的线程上任意重新排序

详细信息请阅读规范,特别是与易失性访问相关的副作用。如果在那之后你还有更多的问题,那就发布一个新问题。这是非常棘手的事情

此外,这个问题的前提是,您忽略了决定任务完成的现有机制,而不是您自己的机制。现有机制由专家设计;使用它们

我看到很多这样编写的代码(没有锁或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();
    }
}

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

一个很好的例子,添加一个volatile确实可以解决这个问题,如果我是,请纠正我wrong@MrinalKamboj是的,volatile会阻止缓存m_Done的值,这导致了示例中的主要问题。我所了解的是,在上面的程序中,对于组合s,t值,我们有四个可能的结果,每个结果都是真/假,这可能只是因为使用了volatile关键字,否则会缓存一个变量,以确保修改后的值不会被其他方法读取,即使它被更改,但会导致预期逻辑出现问题。为了使事情更加确定,可以使用标准说明(如lock)来确保对最终结果的完全保证solution@MrinalKamboj:您能否准确描述读写顺序导致s和t均为真?假设线程一赢得比赛并在线程二调用SetR之前调用GetR。q为真,那么s为真。然后MethodTwo调用SetR,q为false,所以t仍然为false。同样,如果线程2赢得比赛,那么看起来t必须为真,但s必须为假。我在这里的分析是不正确的。s和t可能都是真的。错误在哪里?@Eric Lippert这两个s,t只有在代码执行时才能为真,这样它就可以在任意一个线程上执行if条件,然后在if中计算的变量r,q在另一个线程上重置。由于处理器发现一个线程上正在求值的变量在另一个线程上被修改,并且由于volatile指令而无法缓存该变量,因此它会重新安排调用序列,如果(!GetR())和(!q)在SetR()之前,且q=true。否则,如果我们从逻辑上看,正如你所建议的,它总是一对一错。如果我完全偏离了标准,请告诉我。@Eric Lippert:x86可以交换读取/写入,如果它们不在同一内存区域上工作。对于易失性读取,可以在读取之后移动操作;对于易失性写入,可以在写入之前移动操作。这意味着“q=true”和“GetR()”可以在MethodOne()中交换,如果发生这种情况,MethodTwo()可以在“q”变为true之前设置“r”,从而使“s”和“t”都变为true。@ViktorPeller:没错。C#规范指出,不能保证在没有锁的情况下,可以观察到易失性读写在线程之间具有一致的顺序,事实上,在x86上,即使使用它的强内存模型,我们也可以观察到这种情况。我忍不住觉得
异步
/
等待
的意义在于
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;
}
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();
    }
}