如何在Parallel.ForEach循环中临时同步?

c#

所以这是一个有效但效率低下的代码的最小版本:

Parallel.ForEach(list, x =>
{
    doThing1(x);
});

Thing1Done = true;

Parallel.ForEach(list, x =>
{
    doThing2(x);
});

Thing2Done = true;

Parallel.ForEach(list, x =>
{
    doThing3(x);
});

Thing3Done = true;

直观地说,我想在同一个循环中运行所有 3 个“事物”,但它们必须能够临时同步以更新各自的Thing*n*Done属性。

这个想法的伪代码如下:

Parallel.ForEach(list, x =>
{
    doThing1(x);
    // wait for doThing1 to be completed for all other elements in list
    Thing1Done = true;

    doThing2(x);
    // wait for doThing2 to be completed for all other elements in list
    Thing2Done = true;

    doThing3(x);
    // wait for doThing3 to be completed for all other elements in list
    Thing3Done = true;
});

因此,例如,有必要doThing1()listbefore 的每个成员完成其执行Thing1Done设置为 true。doThing2()只能在Thing1Done设置后开始。

每个单独的步骤都不太昂贵,我担心天真的方法所涉及的开销。假设初始化线程所涉及的开销是我最关心的问题,那么有效解决此任务的最佳方法是什么?如果可能的话,我还想避免忙等待(线程在 while 循环中旋转,直到某些标志设置为 true 才做任何有用的事情)。

如果 Parallel 库不能做我想做的事,我愿意写一些更一般的东西。

回答

您可以使用Barrier线程同步原语:

var barrier = new Barrier(list.Count);

var options = new ParallelOptions()
{
    MaxDegreeOfParallelism = list.Count,
    TaskScheduler = new ThreadPerTask()
};

Parallel.ForEach(list, options, x =>
{
    doThing1(x);
    // wait for doThing1 to be completed for all other elements in list
    barrier.SignalAndWait();

    doThing2(x);
    // wait for doThing2 to be completed for all other elements in list
    barrier.SignalAndWait();

    doThing3(x);
    // wait for doThing3 to be completed for all other elements in list
    barrier.SignalAndWait();
});

SignalAndWait方法发出项目完成的信号,并等待所有项目完成(直到ParticipantsRemaining属性变为零)。在该CurrentPhaseNumber属性增加之后,所有线程同时解除阻塞,并且可以自由地向下一个里程碑竞赛。

这是处理 的项目的一种极其低效的方式list,因为它需要每个项目有一个专用线程。您将需要一个自定义TaskScheduler来实现此设置,如下所示:

public class ThreadPerTask : TaskScheduler
{
    protected override void QueueTask(Task task)
    {
        new Thread(() => this.TryExecuteTask(task))
        {
            IsBackground = true
        }.Start();
    }

    protected override bool TryExecuteTaskInline(Task task,
        bool taskWasPreviouslyQueued) => false;

    protected override IEnumerable<Task> GetScheduledTasks() { yield break; }
}

恕我直言,您的第一种方法,即使用多个连续Parallel.ForEach循环的方法,是解决此问题的正确方法。


以上是如何在Parallel.ForEach循环中临时同步?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>