可以使用C#Can Barrier或类似的东西多次同步任务吗?

时间:2018-04-01 15:00:05

标签: c#

我知道在第二阶段运行之前,可以使用Barrier让几个任务同步完成。

我希望有几个任务可以同步多个步骤:

state is 1;
Task1 runs and pauses waiting for state to become 2;
Task2 runs and pauses waiting for state to become 2;
Task2 is final Task and causes the state to progress to state 2;
Task1 runs and pauses waiting for state to become 3;
Task2 runs and pauses waiting for state to become 3;
Task2 is final Task and causes the state to progress to state 3;
state 3 is final state and so all tasks exit.

我知道我可以在每个州结束时启动新任务,但由于每项任务都不会花费太长时间,我想避免为每一步创建新任务。

我可以使用for循环同步运行上面的命令,但是最终状态可以是100,000,所以我想利用多个线程来更快地运行进程,因为进程是CPU绑定的。

我尝试使用计数器来跟踪完成后每个任务增加的已完成任务的数量。如果Task是要完成的最终任务,那么它将把状态更改为下一个状态。所有已完成的任务然后等待使用while (iterationState == state) await Task.Yield,但性能很糟糕,在我看来这是一种非常粗暴的方式。

完成上述工作的最有效方法是什么?必须有一个优化的工具来完成这项工作吗?

我使用Parallel.For,创建300个任务,每个任务需要运行多达100,000个状态。运行在一个状态的每个任务在不到一秒的时间内完成,创建300 * 100,000个任务是一个巨大的开销,使得即使使用单个线程也可以更快地同步运行整个事务。

所以我想创建300个任务,让这些任务同步移动到100,000个状态。希望创建仅300个任务而不是300 * 100,000个任务的开销,以及在任务之间优化同步的开销,将比在单个线程上同步执行任务时运行得更快。

每个州必须完全完成才能运行下一个州。

那么 - 这种情况的最佳同步技术是什么?谢谢!

2 个答案:

答案 0 :(得分:2)

while (iterationState == state) await Task.Yield确实是在300个任务中同步的一个糟糕的解决方案(不,300不一定非常昂贵:你只能获得合理数量的线程分配)。

这里的关键问题不是Parallel.For,而是在300个任务之间进行同步,以便有效地等待每个任务完成给定阶段。

这里最简单,最干净的解决方案可能是在阶段和并行上有一个for循环。对于你想并行化的位:

for (int stage = 0; stage < 10000; stage++)
{
   // the next line blocks until all 300 have completed
   // will use thread pool threads as necessary
   Parallel.For( ... 300 items to process this stage ... );
}

不需要额外的同步原语,没有旋转等待消耗CPU,线程之间没有不必要的颠簸,试图查看它们是否准备好进展。

答案 1 :(得分:1)

我想我正在理解你要做的事情,所以这里有一个建议的方法来处理它。注意 - 我使用Action作为阻止集合的类型,但您可以将其更改为在您的方案中最有效的方法。

// Shared variables
CountdownEvent workItemsCompleted = new CountdownEvent(300);
BlockingCollection<Action> workItems = new BlockingCollection<Action>();
CancellationTokenSource cancelSource = new CancellationTokenSource();

// Work Item Queue Thread
for(int i=1; i < stages; ++i)
{
    workItemsCompleted.Reset(300);
    for(int j=0; j < workItemsForStage[i].Count; ++j)
    {
         workItems.Add(() => {}) // Add your work item here
    }
    workItemsCompleted.Wait(token) // token should be passed in from cancelSource.Token
}

// Worker threads that are making use of the queue
// token should be passed to the threads from cancelSource.Token
while(!token.IsCancelled)
{
    var item = workItems.Take(token); // Blocks until available item or token is cancelled
    item();
    workItemsCompleted.Signal();
}

如果需要,您可以使用主线程中的cancelSource取消正在运行的操作。在您的工作线程中,您需要处理OperationCancelledException。通过此设置,您可以根据需要启动任意数量的工作线程,并轻松地在获得最佳性能的位置进行基准测试(可能仅使用10个工作线程等)。只需启动任意数量的工作程序,然后在工作项队列线程中排队工作项。它基本上是一个生产者 - 消费者类型模型,除了生产者排队工作的一个阶段,然后阻塞直到该阶段完成,然后排队下一轮工作。