【问题标题】:Paraller.ForEach does not work for meParallel.ForEach 对我不起作用
【发布时间】:2015-05-18 19:02:42
【问题描述】:

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

我认为 lock 会使它不平行,但我仍然遇到问题。 我认为它的行为与以下内容相同:

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

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

【问题讨论】:

  • 它会强制对代码进行序列化。显然,您的问题不在于代码的并行化,而在于其他问题。如果不知道你在做什么,或者没有一个可复制的例子,我们不可能说出你做错了什么。
  • 序列化?你能告诉更多吗?我现在要回家了,如果可能的话,明天我会添加更多细节,因为我有很多代码。
  • 不,我们无法告诉您更多信息,即使没有并行化,您的代码也无法运行。它有各种各样的问题,但同时运行的迭代不是其中之一。
  • 表示一次只运行一个循环,但顺序可能不同。您的代码很可能取决于顺序。
  • @juharr:你说得对,问题解决了。

标签: c# parallel-processing parallel.foreach


【解决方案1】:

关于您粘贴的 sn-p 代码的串行执行,您的假设是不正确的。

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

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

将您的 Parallel.ForEach 替换为 ThreadPool 可能会更容易理解:

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

这两个 sn-ps 或多或少是等价的。如您所见,您首先为列表中的每个项目启动一个线程,然后在线程内部,您获得锁,这将确保没有其他线程可以访问该封闭的关键部分。然而,这并不能保证它们是按顺序完成的并且顺序会被保留。

线程池中线程的执行顺序不受您的控制,因此,使用线程池无法保证任何顺序(至少,不是传统意义上的)。

现在让我们看一下这个例子,希望能让事情变得更清楚:

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

【讨论】:

  • 呼吸新鲜空气 5 分钟后,我解决了我的问题。这正是你所说的。任务以或多或少的随机顺序执行。我正在计算浮点数。所以通常使用浮点数:A+B+C != A+C+B (如果 C != B)。这个问题给我的计算带来了非常小的差异。感谢您的详细帖子。
【解决方案2】:

将您的代码重写为与您的Parallel.ForEach 等效的 PLINQ:

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

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

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

【讨论】:

    猜你喜欢
    • 2017-09-29
    • 2015-03-05
    • 2018-06-14
    • 2014-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多