使用Interlocked的意外结果

时间:2016-03-10 19:19:44

标签: c# .net multithreading

我有这段代码

int j = -1;
Parallel.For(0, 100, i =>
{
    Console.Write("i= " +i);
    Interlocked.Increment(ref j);
    Console.Write(" j= " + j);
    Console.WriteLine();
});

运行之后,我希望以某种方式随机显示i的值,而不是按升序排列,但对于j变量的值,我期望它始终拥有它们按升序排列。

所以现在我有了这个输出:

enter image description here

因此j= 7893之间存在94,这是对的吗?你们有些人可以解释一下为什么我这里有这个值而不是7779之间,这是因为Console.Write方法不是线程安全的吗?

j变量的值是否按照我预期的那样递增,而输出j = 78只是Console.Write方法的一个小故障,因为它不是线程安全的?< / p>

2 个答案:

答案 0 :(得分:6)

  

表示&#34; j&#34;变量我期望它们一直按升序排列。

它不仅不能保证,甚至不能保证每一个数字只打印一次。它可以多次打印一个数字,或者根本不打印。一个线程可以增加数字(让我们说从0到1),然后暂停,让另一个线程运行,然后再次增加数字(到2),打印它,然后暂停,让第一个线程恢复,并再次打印相同的值(在这种情况下为2)。

同样,一个线程可以将变量解析为一个值,然后暂停,让另一个线程增加数十次,然后打印它,然后让原始线程恢复,打印它从变量中提取的值很久以前所有这些增量。

从根本上说,变量的增量和值的打印不是原子操作。您需要显式同步该代码块,以防止多个线程同时处于这两个操作的中间,以便获得您正在描述的行为。

答案 1 :(得分:1)

Interlocked.Increment唯一保证的是,没有两个线程会同时增加j。除此之外,没有办法确定哪个线程将首先写入控制台。 看起来你正在进行实验,顺序对你来说并不重要。这很好,因为它是多线程应用程序如何以非确定性方式运行的一个很好的例子。在这种情况下,如果您希望这些事情发生,它就会很好用,如果您希望它们以可预测的顺序发生,那就不行。

这不会修复i,但您可以尝试

var k = Interlocked.Increment(ref j);
Console.Write(" k= " + k);
Console.WriteLine();

这仍然不能保证顺序,但它应该能为您提供所有独特的价值。