Neshta
Neshta

Reputation: 2745

Stopping Parallel.ForEach if one of threads performs more than N minutes

I'm looking for a solution for stopping Parallel.ForEach if one of threads performs more than 2 minutes.

The next solution I think is not very good because of x2 extra threads:

Parallel.ForEach(items, (item, opt) =>
{
    var thread = new Thread(() => { /* a process */ });
    thread.Start();

    bool finished = thread.Join(TimeSpan.FromMinutes(2));
    if (!finished)
    {
        thread.Abort();
        opt.Stop();
    }
});

Do you know a better solution for the issue?

Upvotes: 3

Views: 1427

Answers (1)

VMAtm
VMAtm

Reputation: 28355

First of all, I want to note that Parallel class will not create a thread for each of your item, it will use default ThreadPool, which has by default number of threads equal to processor's cores count. Other problem in your code is that you do not stop all the tasks after 2 minutes of working, you only cancel the one which you've waited for two minutes.

I suggest you remove the Thread usage from your code, and create an array of Tasks with single CancellationToken for them with a Timeout for it or with a timeout for TaskFactory, and start them all. Also your code should explicitly check the token for cancellation pending.

So your code could be something like this:

var cts = new CancellationTokenSource();
// two minutes in milliseconds
cts.CancelAfter(2000 * 60);

var tasks = new List<Task>();
foreach (var item in items)
{
    // this is needed because of closures work in C#
    var localItem = item;
    tasks.Add(Task.Run(() => 
        { /* a process with a localItem here */ 
            // this check should be repeated from time to time in your calculations
            if (cts.Token.IsCancellationRequested)
            {
                cts.Token.ThrowIfCancellationRequested();
            }
        }
    // all tasks has only one token
    , cts.Token)

}
// this will cancel all tasks after 2 minutes from start
Task.WaitAll(tasks.ToArray(), TimeSpan.FromMinutes(2));
// this will cancel all tasks if one of them will last more than 2 minutes
Task.WaitAll(tasks.ToArray());

Update:

As you said that the each item is independent, you can create CancellationTokenSource for each task, but, as @ScottChamberlain noted, in this case too many tasks will run in the same time. You can write your own TaskScheduler, use some Semafor (or it's slim version) or simply use the Parallel class with ParallelOptions.MaxDegreeOfParallelism correctly set.

Upvotes: 2

Related Questions