Paraller.ForEach 对我不起作用

Paraller.ForEach does not work for me

我有一些代码 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
});

在这种情况下,锁怎么可能不破坏并行性?

将您的代码重写为 PLINQ,这等同于您的 Parallel.ForEach:

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

然后将WithDegreeOfParallelism(1)添加到"KILL"并行度。所以你可以看到你的错误是来自并行 运行 线程还是来自其他东西。

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

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

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 线程管理和调度的性质,执行顺序是随机的而不是顺序的。