我正在尝试测试c# parallel
方法,这是我的测试程序:
class Program
{
static int counter;
static void Main(string[] args)
{
counter = 0;
Parallel.Invoke(
() => func(1),
() => func(2),
() => func(3)
);
Console.Read();
}
static void func(int num)
{
for (int i = 0; i < 5;i++ )
{
Console.WriteLine(string.Format("This is function #{0} loop. counter - {1}", num, counter));
counter++;
}
}
}
我尝试做的是有一个静态共享变量,每个函数实例将它增加1。
我预计counter
将按(1,2,3,...)
顺序打印但是输出结果令人惊讶:
This is function #1 loop. counter - 0
This is function #1 loop. counter - 1
This is function #1 loop. counter - 2
This is function #1 loop. counter - 3
This is function #1 loop. counter - 4
This is function #3 loop. counter - 5
This is function #2 loop. counter - 1
This is function #3 loop. counter - 6
This is function #3 loop. counter - 8
This is function #3 loop. counter - 9
This is function #3 loop. counter - 10
This is function #2 loop. counter - 7
This is function #2 loop. counter - 12
This is function #2 loop. counter - 13
This is function #2 loop. counter - 14
谁能给我解释一下为什么会这样? 问题是你的代码不是线程安全的。例如,可能发生的情况是:
- 函数#2获取
counter
的值以在Console.WriteLine()
中使用它 - 函数#1获取
counter
的值,调用Console.WriteLine()
,增加counter
- 函数#1获取
counter
的值,调用Console.WriteLine()
,增加counter
- 函数#2最终使用旧值 调用
Console.WriteLine()
而且,++
本身不是线程安全的,所以最终值可能不是15。
要解决这两个问题,可以使用Interlocked.Increment()
:
for (int i = 0; i < 5; i++)
{
int incrementedCounter = Interlocked.Increment(ref counter);
Console.WriteLine("This is function #{0} loop. counter - {1}", num, incrementedCounter);
}
这样,您将在递增之后获得数字,而不是像在原始代码中那样在递增之前。同样,这段代码仍然不会按照正确的顺序打印数字,但是可以确保每个数字只打印一次。
如果你想让数字按正确的顺序排列,你需要使用lock
:
private static readonly object lockObject = new object();
…
for (int i = 0; i < 5; i++)
{
lock (lockObject)
{
Console.WriteLine("This is function #{0} loop. counter - {1}", num, counter);
counter++;
}
}
当然,如果你这样做,你实际上不会得到任何并行性,但我假设这不是你真正的代码。
实际上发生了什么- Invoke只是将这些任务排队,并且运行时为这些任务分配线程,这给了它很多随机元素(哪一个将首先被拾取等)。
连msdn的文章都这么说:
这个方法可以用来执行一组操作,可能是并行的。不保证操作执行的顺序或它们是否并行执行。此方法在提供的每个操作完成之前都不会返回,无论完成是由于正常终止还是异常终止。
这个问题看起来像是许多线程访问同一个变量。这是并发性的问题。你可以试试:
static object syncObj = new object();
static void func(int num)
{
for (int i = 0; i < 5; i++)
{
lock (syncObj)
{
Console.WriteLine(string.Format("This is function #{0} loop. counter - {1}", num, counter));
counter++;
}
}
}