What's the best way of achieving a parallel infinite Loop?
Asked Answered
H

4

11

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.
});
Hirohito answered 29/12, 2011 at 18:26 Comment(2)
are there any Threading model that you could consumeFungiform
Hi @redcalx. Including answers inside the questions tends to be messy. I would suggest to remove the answer from the question, and post it as a self-answer instead.Rigby
S
1

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.

Stenography answered 29/12, 2011 at 18:30 Comment(2)
The context for this problem is that it's a data mining project. I'm taking data analysis code that currently runs indefinitely (e.g run over night and then signal it to stop the next morning) and farming it off to multiple threads, as such I do want it to run indefinitely on all cores.Hirohito
@locster - Have a master "infinite" thread that creates child thread to its work.Depone
S
1

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...

Scruggs answered 29/12, 2011 at 18:41 Comment(0)
R
0

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();
});
Rigby answered 24/6, 2022 at 22:55 Comment(0)
A
0

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"));  
                   }));  
          }
Adore answered 8/3, 2023 at 13:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.