Interlocked.CompareExchange(ref value,newValue,compareTo)

时间:2016-01-01 19:02:16

标签: c# multithreading visual-studio-2015 task-parallel-library

using System;
using System.Threading;
using System.Threading.Tasks;

namespace _1._41_Compare_and_Exchange_as_a_nonAtomic_operation
{
    public class Program
    {
        static int value = 1;

        public static void Main()
        {

                Task t1 = Task.Run(() =>
                {
                    if (value == 1)
                    {

                    Thread.Sleep(1000);

                        value = 2;

                    }
                });

                Task t2 = Task.Run(() =>
                {
                    value = 3;

                });

                Task.WaitAll(t1, t2);
                Console.WriteLine(value); //Displays 2

        }
    }
}

我正在尝试将上述非原子操作转换为原子操作,使用:

Interlocked.CompareExhange(ref value, newValue, compareTo);

我写的是

 Interlocked.CompareExhange(ref value, value, value); //This doesn't look right!

Interlocked.CompareExhange(ref value, t2, t1); //will not compile

问题

  1. 有或没有Interlocked.CompareExchange值输出为2?为什么?
  2. 参考t1,t2的正确方法是什么?
  3. 为什么我不能直接引用任务的输出? (值,t2,t1)
  4. 是否需要进行某种转换?
  5. 据我所知,值应该更新为2(值= 1)然后更新为3而不是当前输出(值= 1,更新为3然后将其更新为2)一旦使用Interlocked.CompareExchange

1 个答案:

答案 0 :(得分:2)

  

有或没有Interlocked.CompareExchange值输出为2?为什么呢?

真正的答案是:巧合。您的代码是非确定性的,其行为取决于OS线程调度程序。

但是...... t1t2之前开始执行实际上并不奇怪。在这种情况下,if (value == 1)检查是在t2有机会执行value = 3;之前进行的。

总而言之,最可能的时间表是:

  • t1:针对value
  • 检查1
  • t1t1入睡一秒
  • t2value设置为3
  • t1:稍后醒来
  • t1value设置为2

但正如我上面所说,这就是实践中发生的事情,但是游览代码仍然不确定,因为t2 原则上可以它在t1之前执行。

  

引用t1,t2的正确方法是什么?

如果我正确理解你的问题,你的方式似乎是正确的。

  

为什么我不能直接引用任务的输出? (值,t2,t1)

您启动void个任务,这些任务首先不会拥有输出。它们由Task类型表示。

您可以启动一个返回如下结果的任务:

var t = Task.Run(() => {
    // do anything
    return 42;
});

在这种情况下,t将为Task<int>类型,并且您可以在任务结束时访问其Result属性(如果您尝试在任务之前访问i)完成后,它将一直阻止,直到任务结束。)

  

是否需要进行某种转换?

我不确定我在这里理解你的问题。

  

据我所知,值应更新为2(值= 1)然后更新为3而不是当前输出(值= 1,更新为3然后将其更新为2)一旦使用Interlocked.CompareExchange?

差不多,是的。 Interlocked.CompareExchange atomic 。它将执行比较在硬件级别一步设置值:

Interlocked.CompareExchange(ref value, 2, 1);

这是 atomic 等价于:

if (value == 1)
    value = 2;

如果你这样做,最终的值总是3。因为两种不同的场景是可能的(这很简单,因为现在两个任务都包含一个原子语句):

  • t1t2之前执行:

    • t1:针对value检查1,这是真的,因此value设置为2
    • t2value设置为3
  • t2t1之前执行:

    • t2value设置为3
    • t1:针对value检查1,这是假的,因此value保持不变

正如您所看到的,在这两种情况下,您最终都会得到3