Paraller.ForEach对我不起作用

时间:2015-05-18 19:02:43

标签: c# parallel-processing parallel.foreach

我对某些代码有Parallel.ForEach,但它给出了与非并行代码不同的结果。因此,出于诊断目的,我使用lock关键字包装整个代码:

var someArray = new double[123];
var syncObject = new Object();

Parallel.ForEach (windows, (win) =>
{
    lock (syncObject) // now it should execute sequentially?
    {
          // do something with someArray
    }
});

我认为锁会使它不平行,但我仍然遇到问题。 我认为它的行为与:

相同
windows.ToList().ForEach ( (win) =>
{
    // do something with someArray
});

在这种情况下锁怎么可能不会杀死并行性?

2 个答案:

答案 0 :(得分:1)

关于您已粘贴的代码段的代码的串行执行,您的假设不正确。

Parallel.ForEach (windows, (win) =>
{
    lock (syncObject) // now it should execute sequentially?
    {
          // do something with someArray
    }
});

您放置的lock确保一次只有一个线程可以访问代码的这个特定关键部分(lock(syncObject) {}中包含的代码,但并不意味着语句本身将按顺序执行。

用ThreadPool替换你的Parallel.ForEach,可能会让它更容易理解:

foreach(var item in list)
{
      ThreadPool.QueueUserWorkItem(i =>
                                       {
                                           lock (syncObject)
                                           {
                                               // do something with i here
                                           }
                                       }, item);
 }

这两个片段或多或少相同。如您所见,首先为列表中的每个项启动一个线程,然后在线程内部获取锁,这将确保没有其他线程可以访问该封闭的临界区。然而,这并不能保证它们是按顺序完成的,并且保留了顺序。

线程池中线程的执行顺序超出了您的控制范围,因此,不可能保证使用线程池进行任何排序(至少不是传统意义上的。)

现在让我们来看看这个让希望更清晰的例子:

var syncObject = new Object();
var list = new List<int>();
for(int i=0;i<20;i++)
{
    list.Add(i);
}

Parallel.ForEach(list, item =>
{
    Console.WriteLine(item + " waiting to be executed on " + Thread.CurrentThread.ManagedThreadId);
    lock (syncObject) // now it should execute sequentially?
    {
        Console.WriteLine(item + " executing on " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(100);

     }
 });

此执行的结果与此类似:

0 waiting to be executed on 1
0 executing on 1
2 waiting to be executed on 4
4 waiting to be executed on 6
10 waiting to be executed on 9
12 waiting to be executed on 5
8 waiting to be executed on 10
16 waiting to be executed on 7
6 waiting to be executed on 8
14 waiting to be executed on 3
1 waiting to be executed on 11
2 executing on 4
10 executing on 9
3 waiting to be executed on 4
11 waiting to be executed on 9
16 executing on 7
14 executing on 3
17 waiting to be executed on 7
15 waiting to be executed on 3
8 executing on 10
9 waiting to be executed on 10
4 executing on 6
12 executing on 5
5 waiting to be executed on 6
6 executing on 8
13 waiting to be executed on 5
1 executing on 1
7 waiting to be executed on 8
3 executing on 4
18 waiting to be executed on 1
11 executing on 9
17 executing on 7
15 executing on 3
9 executing on 10
5 executing on 6
13 executing on 5
7 executing on 8
18 executing on 1
19 waiting to be executed on 1
19 executing on 1

正如您所看到的,可能有多个线程等待进入临界区,但在任何给定时间只有一个线程将执行lock内的语句。 但是由于ThreadPool线程管理和调度的性质,执行的顺序是随机的而不是顺序的。

答案 1 :(得分:0)

将您的代码重写为PLINQ,相当于您的Parallel.ForEach

        windows
            .AsParallel()
            .ForAll((win) =>
        {
            //DoSomething
        });

然后将WithDegreeOfParallelism(1)添加到“KILL”并行度。因此,您可以看到您的错误来自平行运行的线程或来自其他东西。

        windows
            .AsParallel()
            .WithDegreeOfParallelism(1)
            .ForAll((win) =>
        {
            //DoSomething
        });