【发布时间】:2012-11-14 02:44:05
【问题描述】:
有一天我试图更好地理解线程概念,所以我写了几个测试程序。其中之一是:
using System;
using System.Threading.Tasks;
class Program
{
static volatile int a = 0;
static void Main(string[] args)
{
Task[] tasks = new Task[4];
for (int h = 0; h < 20; h++)
{
a = 0;
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = new Task(() => DoStuff());
tasks[i].Start();
}
Task.WaitAll(tasks);
Console.WriteLine(a);
}
Console.ReadKey();
}
static void DoStuff()
{
for (int i = 0; i < 500000; i++)
{
a++;
}
}
}
我希望我能看到小于 2000000 的输出。我想象中的模型如下:更多线程同时读取变量 a,a 的所有本地副本都将相同,线程递增它并写入发生并且一个或多个增量以这种方式“丢失”。
虽然输出与这个推理背道而驰。一个示例输出(来自 corei5 机器):
2000000
1497903
1026329
2000000
1281604
1395634
1417712
1397300
1396031
1285850
1092027
1068205
1091915
1300493
1357077
1133384
1485279
1290272
1048169
704754
如果我的推理是正确的,我偶尔会看到 2000000,有时数字会少一些。但我偶尔看到的是 2000000,而数字远小于 2000000。这表明幕后发生的不仅仅是几个“增量损失”,而是更多的事情正在发生。有人可以解释一下情况吗?
编辑: 当我编写这个测试程序时,我完全知道如何使这个线程安全,并且我希望看到小于 2000000 的数字。让我解释一下为什么我对输出感到惊讶:首先让我们假设上面的推理是正确的。第二个假设(这很可能是我困惑的根源):如果发生冲突(并且确实发生了),那么这些冲突是随机的,我希望这些随机事件的发生在某种程度上是正态分布。在这种情况下,输出的第一行表示:从 500000 次实验中,随机事件从未发生过。第二行说:随机事件至少发生了 167365 次。 0 和 167365 之间的差异太大(正态分布几乎不可能)。所以案件归结为以下几点: 两个假设之一(“增量损失”模型或“有点正态分布的并行冲突”模型)是不正确的。是哪一个?为什么?
【问题讨论】:
-
请记住,任务不是线程,它们是更高级别的抽象。见stackoverflow.com/questions/4130194/…。
-
你真的应该在第一个内部循环中使用
Task.Factory.StartNew(DoStuff):p -
@Baboon 如果你想真的学究气,那么你会使用在.NET 4.5中引入的
Task.Run。 -
当我运行你的程序时,我看到一致的数字在大约。 1.1 和 180 万。你不可能在没有竞争条件的情况下进行 200 万次增量......你能重现你的数字吗?
-
@Jan 在两次单数运行之间不可能重现数字,更不用说在不同机器上运行了;这一切都取决于处理器何时获得变量,每次都是不确定的(取决于发生了什么)。重点是,values are being overwritten because of the increment operator (
++)。
标签: c# concurrency task-parallel-library