Parallel.Foreach C# 暂停和停止函数?

发布于 2024-12-20 17:49:19 字数 133 浏览 6 评论 0 原文

暂停和停止(在结束之前)parallel.foreach 的最有效方法是什么?

Parallel.ForEach(list, (item) =>
{
    doStuff(item);
});

What would be the most effective way to pause and stop (before it ends) parallel.foreach?

Parallel.ForEach(list, (item) =>
{
    doStuff(item);
});

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

(り薆情海 2024-12-27 17:49:19

Damien_The_Unbeliver 有一个很好的方法,但前提是您想让某些外部进程停止循环。如果您想让循环像在正常的 forforeach 循环中使用 break 一样中断,您将需要使用 具有 href="http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallelloopstate.aspx" rel="nofollow noreferrer">ParallelLoopState 作为一个循环体的参数。 ParallelLoopState 有两个与您想要执行的操作相关的函数,Stop()Break()

函数 Stop()在系统方便的时候尽早停止处理元素,这意味着在调用 Stop() 后可以执行更多迭代,并且不能保证处理元素在您停止的元素甚至开始处理之前出现。

函数 Break() 的执行方式与 Stop() 完全相同,但它也会评估位于该项目之前的 IEnumerable 的所有元素。您调用了 Break()。当您不关心元素的处理顺序,但必须处理所有元素直到停止点时,这非常有用。

检查从 foreach 返回的 ParallelLoopResult查看 foreach 是否提前停止,以及是否使用了 Break(),它处理的最小编号的项目是什么。

Parallel.ForEach(list, (item, loopState) =>
    {
        bool endEarly = doStuff(item);
        if(endEarly)
        {
            loopState.Break();
        }
    }
    );
//Equivalent to the following non parallel version, except that if doStuff ends early
//    it may or may not processed some items in the list after the break.
foreach(var item in list)
{
    bool endEarly = doStuff(item);
    if(endEarly)
    {
        break;
    }
}

这是一个更实际的例子,

static bool[] list = new int[]{false, false, true, false, true, false};

long LowestElementTrue()
{
    ParallelLoopResult result = Parallel.ForEach(list, (element, loopState) =>
    {
        if(element)
            loopState.Break();
    }
    if(result.LowestBreakIteration.IsNull)
        return -1;
    else
        return result.LowestBreakIteration.Value;
}   

无论它如何分配工作,它总是返回 2 作为答案。

假设处理器调度两个线程来处理此问题,第一个线程处理元素 0-2,第二个线程处理元素 3-5。

Thread 1:                Thread 2
0, False, continue next  3, False, continue next
1, False, continue next  4, True, Break
2, True, Break           5, Don't process Broke

现在,调用的最低索引 Break 是 2,因此 ParallelLoopResult.LowestBreakIteration 每次都会返回 2,无论线程如何分解,因为它始终会处理到数字 2。

这里是一个示例如何使用停止。

static bool[] list = new int[]{false, false, true,  false, true, false};

long FirstElementFoundTrue()
{
    long currentIndex = -1;
    ParallelLoopResult result = Parallel.ForEach(list, (element, loopState, index) =>
    {
        if(element)
        {
             loopState.Stop();

             //index is a 64 bit number, to make it a atomic write
             // on 32 bit machines you must either:
             //   1. Target 64 bit only and not allow 32 bit machines.
             //   2. Cast the number to 32 bit.
             //   3. Use one of the Interlocked methods.
             Interlocked.Exchange (ref currentIndex , index);
        }
    }
    return currentIndex;
}   

根据它如何分配工作,它将返回 2 或 4 作为答案。

假设处理器调度两个线程来处理此问题,第一个线程处理元素 0-2,第二个线程处理元素 3-5。

Thread 1:                 Thread 2
0, False, continue next    3, False, continue next
1, False, continue next    4, True, Stop
2, Don't process, Stopped  5, Don't process, Stopped

在本例中,它将返回 4 作为答案。让我们看看相同的过程,但它是否处理所有其他元素而不是 0-2 和 3-5。

Thread 1:                   Thread 2
0, False, continue next     1, False, continue next
2, True, Stop               3, False, continue next
4, Don't process, Stopped   5, Don't process, Stopped

这次它将返回 2 而不是 4。

Damien_The_Unbeliver has a good method, but that is only if you want to have some outside process stop the loop. If you want to have the loop break out like using a break in a normal for or foreach loop you will need to use a overload that has a ParallelLoopState as one of the parameters of the loop body. ParallelLoopState has two functions that are relevant to what you want to do, Stop() and Break().

The function Stop() will stop processing elements at the system's earliest convenience which means more iterations could be performed after you call Stop() and it is not guaranteed that the elements that came before the element you stopped at have even begun to process.

The function Break() performs exactly the same as Stop() however it will also evaluate all elements of the IEnumerable that came before the item that you called Break() on. This is useful for when you do not care what order the elements are processed in, but you must process all of the elements up to the point you stopped.

Inspect the ParallelLoopResult returned from the foreach to see if the foreach stopped early, and if you used Break(), what is the lowest numbered item it processed.

Parallel.ForEach(list, (item, loopState) =>
    {
        bool endEarly = doStuff(item);
        if(endEarly)
        {
            loopState.Break();
        }
    }
    );
//Equivalent to the following non parallel version, except that if doStuff ends early
//    it may or may not processed some items in the list after the break.
foreach(var item in list)
{
    bool endEarly = doStuff(item);
    if(endEarly)
    {
        break;
    }
}

Here is a more practical example

static bool[] list = new int[]{false, false, true, false, true, false};

long LowestElementTrue()
{
    ParallelLoopResult result = Parallel.ForEach(list, (element, loopState) =>
    {
        if(element)
            loopState.Break();
    }
    if(result.LowestBreakIteration.IsNull)
        return -1;
    else
        return result.LowestBreakIteration.Value;
}   

No matter how it splits up the work it will always return 2 as an answer.

let's say the processor dispatches two threads to process this, the first thread processes elements 0-2 and the 2nd thread processes elements 3-5.

Thread 1:                Thread 2
0, False, continue next  3, False, continue next
1, False, continue next  4, True, Break
2, True, Break           5, Don't process Broke

Now the lowest index Break was called from was 2 so ParallelLoopResult.LowestBreakIteration will return 2 every time, no-matter how the threads are broken up as it will always process up to the number 2.

Here an example how how Stop could be used.

static bool[] list = new int[]{false, false, true,  false, true, false};

long FirstElementFoundTrue()
{
    long currentIndex = -1;
    ParallelLoopResult result = Parallel.ForEach(list, (element, loopState, index) =>
    {
        if(element)
        {
             loopState.Stop();

             //index is a 64 bit number, to make it a atomic write
             // on 32 bit machines you must either:
             //   1. Target 64 bit only and not allow 32 bit machines.
             //   2. Cast the number to 32 bit.
             //   3. Use one of the Interlocked methods.
             Interlocked.Exchange (ref currentIndex , index);
        }
    }
    return currentIndex;
}   

Depending how it splits up the work it will either return 2 or 4 as the answer.

let's say the processor dispatches two threads to process this, the first thread processes elements 0-2 and the 2nd thread processes elements 3-5.

Thread 1:                 Thread 2
0, False, continue next    3, False, continue next
1, False, continue next    4, True, Stop
2, Don't process, Stopped  5, Don't process, Stopped

In this case it will return 4 as the answer. Lets see the same process but if it processes every other element instead of 0-2 and 3-5.

Thread 1:                   Thread 2
0, False, continue next     1, False, continue next
2, True, Stop               3, False, continue next
4, Don't process, Stopped   5, Don't process, Stopped

This time it will return 2 instead of 4.

熟人话多 2024-12-27 17:49:19

为了能够停止 Parallel.ForEach,您可以使用接受 ParallelOptions 参数,并包含一个 CancellationToken 在这些选项中。

有关详细信息,请参阅取消

至于暂停,一般来说,我不明白你为什么要这样做。您可能正在寻找Barrier(用于协调多个线程之间的工作,比如说它们是否都需要在继续 B 部分之前完成 A 部分),但我认为您不会将其与 Parallel.ForEach 一起使用,因为您不这样做不知道会有多少参与者 是。

To be able to stop a Parallel.ForEach, you can use one of the overloads that accepts a ParallelOptions parameter, and include a CancellationToken in those options.

See Cancellation for more details.

As for pausing, I can't think why you'd want to do that, in general. You might be looking for a Barrier (which is used to coordinate efforts between multiple threads, say if they all need to complete part A before continuing to part B), but I wouldn't think that you'd use that with Parallel.ForEach, since you don't know how many participants there will be.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文