实现并行无限循环的最佳方法是什么?

发布于 2024-12-23 12:18:48 字数 1179 浏览 1 评论 0原文

我已经习惯在 .NET 并行扩展中使用 Parallel.For() ,因为这是一种并行化代码的简单方法,而无需手动启动和维护线程(这可能很繁琐)。我现在正在查看一个无限循环(执行某些操作,直到我发出信号停止),我希望并行化,没有无参数的 Parallel.For() 重载来执行此操作,所以是想知道这里最好的方法是什么。原则上我可以做类似的事情:

Parallel.For(0, int.Max)

但我怀疑这可能不是工作分区逻辑处理的预期/有效模式(?)

另一种选择是这样的:

for(;;)
{
    Parallel.For(0, 128, delegate()
    {
       // Do stuff.
    }
}

但这看起来不优雅,也可能导致工作效率低下分区。

现在我的本能是通过创建和维护自己的线程来手动完成此操作,但我有兴趣获得一些对此的反馈/意见。谢谢。

===更新===

我正在使用接受的答案中文章中的代码的简化版本(我已删除 ParallelOptions 参数)。这是代码...

public class ParallelUtils
{
    public static void While(Func<bool> condition, Action body) 
    { 
        Parallel.ForEach(IterateUntilFalse(condition), ignored => body()); 
    }

    private static IEnumerable<bool> IterateUntilFalse(Func<bool> condition) 
    { 
        while (condition()) yield return true; 
    }
}

示例用法是:

Func<bool> whileCondFn = () => !_requestStopFlag;
ParallelUtils.While(whileCondFn, delegate()
{
    // Do stuff.
});

I've gotten used to using Parallel.For() in .NET's parallel extensions as it's a simple way of parallelizing code without having to manually start and maintain threads (which can be fiddly). I'm now looking at an infinite loop (do something until I signal it to stop) that I wish to parallelize, there isn't an argument free Parallel.For() overload to do this so was wondering what the best approach here would be. In principle I could just do something like:

Parallel.For(0, int.Max)

But I'm suspecting that might not be an expected/efficient pattern for the work partitioning logic to handle(?)

Another option is something like:

for(;;)
{
    Parallel.For(0, 128, delegate()
    {
       // Do stuff.
    }
}

But that seems inelegant and may also result in inefficient work partitioning.

Right now my instinct is to do this manually by creating and maintaining my own threads, but I would be interested in getting some feedback/opinions on this. Thanks.

=== UPDATE ===

I'm using a simplified version of the code from the article in the accepted answer (I've removed the ParallelOptions parameter). Here's the code...

public class ParallelUtils
{
    public static void While(Func<bool> condition, Action body) 
    { 
        Parallel.ForEach(IterateUntilFalse(condition), ignored => body()); 
    }

    private static IEnumerable<bool> IterateUntilFalse(Func<bool> condition) 
    { 
        while (condition()) yield return true; 
    }
}

An example usage would be:

Func<bool> whileCondFn = () => !_requestStopFlag;
ParallelUtils.While(whileCondFn, delegate()
{
    // Do stuff.
});

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

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

发布评论

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

评论(4

风启觞 2024-12-30 12:18:48

如果你(真的)想要无限的东西,那么你希望它在尽可能少的核心上。 Parallel.For___ 都不是一个好的选择。

您(可能)需要的是使用 LongRunning 选项创建的单独线程或任务。

然后让它等待信号量,或者作为最后的手段尽可能频繁地调用 Sleep()。

If you (really) want something infinite then you want it on as few cores a possible. None of the Parallel.For___ would be a good choice.

What you (probably) need is a separate Thread or a Task created with the LongRunning option.

And then make it wait on a semaphore, or as a last resort call Sleep() as often as possible.

梦在深巷 2024-12-30 12:18:48

考虑到它是无限微积分请求,但您需要在每个“周期”上有一些有限状态,我想说我会使用外部 for(;;) 循环更改解决方案来执行
Parallel.ForEach(...) 调用某些事件/状态更改。就像 Monitor 信号、event 通知或类似的东西......

Considering that it's infinit calculus request, but you need to have some finit state on every "cycle", I would say that I would change a solution with an external for(;;) loop to execute a
Parallel.ForEach(...) call on some event/state change. Like a Monitor signal, event notification, or something like that...

眼藏柔 2024-12-30 12:18:48

这是 ParallelWhile 方法的现代实现,该方法基于 Parallel.For,并且与 Stephen Toub 的实现 从 2009 年开始。

/// <summary>
/// Executes a while loop in which iterations may run in parallel, loop options
/// can be configured, and the state of the loop can be monitored and manipulated.
/// </summary>
public static void ParallelWhile(
    ParallelOptions parallelOptions,
    Func<bool> condition,
    Action<ParallelLoopState> body)
{
    ArgumentNullException.ThrowIfNull(parallelOptions);
    ArgumentNullException.ThrowIfNull(condition);
    ArgumentNullException.ThrowIfNull(body);

    int workersCount = parallelOptions.MaxDegreeOfParallelism switch
    {
        -1 => Int32.MaxValue, // -1 means unlimited parallelism.
        _ => parallelOptions.MaxDegreeOfParallelism
    };

    Parallel.For(0, workersCount, parallelOptions, (_, state) =>
    {
        while (!state.ShouldExitCurrentIteration)
        {
            if (!condition()) { state.Stop(); break; }
            body(state);
        }
    });
}

主要区别在于 Parallel.For 对每个工作线程执行一次迭代,而不是枚举无限序列。然后,每个线程在内部 while 循环中重复调用 body。这样,与包含虚拟元素的序列交互(缓冲/同步等)的开销就被消除了。如果 body 非常轻量级,则此开销可能会很大。

ParallelWhile 支持 Parallel.For 循环的所有选项,并且在发生异常和取消时具有相同的行为。它还保留 的语义MaxDegreeOfParallelism 选项,当它具有默认值-1时,这意味着无限的并行性,实际上意味着“使用所有可用的并行度” ThreadPool 线程”。我总是赞成显式指定 MaxDegreeOfParallelism,并给它一个合理的值。在大多数情况下,此值是 Environment.ProcessorCount。使ThreadPool饱和会产生许多不良副作用,并且几乎没有带来任何好处(如果有的话)。

使用示例:

ParallelOptions options = new() { MaxDegreeOfParallelism = Environment.ProcessorCount };

ParallelWhile(options, () => true, state =>
{
    if (TryFindTheAnswerToLifeTheUniverseAndEverything(out _)) state.Stop();
});

Here is a modern implementation of a ParallelWhile method, which is based on the Parallel.For, and has identical signature with Stephen Toub's implementation from 2009.

/// <summary>
/// Executes a while loop in which iterations may run in parallel, loop options
/// can be configured, and the state of the loop can be monitored and manipulated.
/// </summary>
public static void ParallelWhile(
    ParallelOptions parallelOptions,
    Func<bool> condition,
    Action<ParallelLoopState> body)
{
    ArgumentNullException.ThrowIfNull(parallelOptions);
    ArgumentNullException.ThrowIfNull(condition);
    ArgumentNullException.ThrowIfNull(body);

    int workersCount = parallelOptions.MaxDegreeOfParallelism switch
    {
        -1 => Int32.MaxValue, // -1 means unlimited parallelism.
        _ => parallelOptions.MaxDegreeOfParallelism
    };

    Parallel.For(0, workersCount, parallelOptions, (_, state) =>
    {
        while (!state.ShouldExitCurrentIteration)
        {
            if (!condition()) { state.Stop(); break; }
            body(state);
        }
    });
}

The main difference is that the Parallel.For executes one iteration per worker-thread, instead of enumerating an infinite sequence. Then each thread invokes the body repeatedly in an inner while loop. This way the overhead of interacting (buffering/synchronizing etc) with a sequence that contains dummy elements is eliminated. This overhead can be significant in case the body is very lightweight.

The ParallelWhile supports all the options of the Parallel.For loop, and it has the same behavior in case of exceptions and cancellation. It also retains the semantics of the MaxDegreeOfParallelism option when it has the default value -1, which means unlimited parallelism, and in practice means "use all the available ThreadPool threads". I am always in favor of specifying explicitly the MaxDegreeOfParallelism, and giving it a reasonable value. In most cases this value is Environment.ProcessorCount. Saturating the ThreadPool has many undesirable side-effects, and offers few benefits, if any, in return.

Usage example:

ParallelOptions options = new() { MaxDegreeOfParallelism = Environment.ProcessorCount };

ParallelWhile(options, () => true, state =>
{
    if (TryFindTheAnswerToLifeTheUniverseAndEverything(out _)) state.Stop();
});
も让我眼熟你 2024-12-30 12:18:48

我发现了这个技巧
https://dejanstojanovic.net/aspnet/2015/september /parallel-infinite-loop-in-c/

    private static IEnumerable<bool> Infinite()  
    {  
        while (true)  
        {  
            yield return true;  
        }  
    } 
    
private static void test()
{
    Parallel.ForEach(Infinite(), new ParallelOptions(), new Action<bool>((val) =>  
                   {  
                       Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));  
                   }));  
          }

i found this trick
https://dejanstojanovic.net/aspnet/2015/september/parallel-infinite-loop-in-c/

    private static IEnumerable<bool> Infinite()  
    {  
        while (true)  
        {  
            yield return true;  
        }  
    } 
    
private static void test()
{
    Parallel.ForEach(Infinite(), new ParallelOptions(), new Action<bool>((val) =>  
                   {  
                       Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));  
                   }));  
          }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文