Pergunta

Code always waits until currently running tasks have finished before the OperationCancelledException is thrown.

I would like the program to stop immediately on the condition being true.

static void Main()
{
    // want to break out of a Parallel.For immediately when a condition occurs
    var cts = new CancellationTokenSource();
    var po = new ParallelOptions();
    po.CancellationToken = cts.Token;

    long counterTotal = 0;

    try
    {
        // want to have a sum of counts at the end
        Parallel.For<long>(1, 26, po, () => 0, delegate(int i, ParallelLoopState state, long counterSubtotal)
                {
                    po.CancellationToken.ThrowIfCancellationRequested();
                    Console.WriteLine(i.ToString());

                    for (int k = 0; k < 1000000000; k++)
                    {
                        counterSubtotal++;

                        if (i == 4 && k == 900000000)
                        {
                            cts.Cancel();
                            // Would like to break out here immediately
                        }
                    }

                    return counterSubtotal;
                }, (x) => Interlocked.Add(ref counterTotal, x)
            );
    }
    catch (OperationCanceledException e)
    {
        Console.WriteLine("Cancelled");
        Console.WriteLine("Total iterations across all threads {0}", String.Format("{0:n0}", counterTotal));
        Console.ReadLine();
    }
}

I found putting a breakpoint on cts.Cancel() and in the catch demonstrates what is happening.

Have looked at state.Stop too.

This is a simplified version of other code.

Perhaps Parallel.For isn't ideal for things which are very long running inside the method if we want to break out immediately.

Update2: The code now works as expected and gives a good total

static void Main()
{
    // want to break out of a Parallel.For immediately when a condition occurs
    var cts = new CancellationTokenSource();
    var po = new ParallelOptions();
    po.CancellationToken = cts.Token;

    long counterTotal = 0;

    try
    {
        // want to have a sum of counts at the end
        // using type param here to make counterSubtotal a long
        Parallel.For<long>(1, 26, po, () => 0, delegate(int i, ParallelLoopState state, long counterSubtotal)
                {
                    Console.WriteLine(i.ToString());
                    // 1 billion
                    for (int k = 0; k < 1000000000; k++)
                    {
                        //po.CancellationToken.ThrowIfCancellationRequested();
                        if (po.CancellationToken.IsCancellationRequested)
                        {
                            return counterSubtotal;
                        }
                        counterSubtotal++;

                        if (i == 4 && k == 400000000)
                        {
                            Console.WriteLine("Inner Cancelled");
                            cts.Cancel();
                        }
                    }
                    return counterSubtotal;
                }, (x) => Interlocked.Add(ref counterTotal, x)
            );
    }
    catch (OperationCanceledException e)
    {
        Console.WriteLine("Cancelled");
        Console.WriteLine("Total iterations across all threads {0}", String.Format("{0:n0}", counterTotal));
        Console.ReadLine();
    }
}
Foi útil?

Solução

If you want it to break more "immediately" than you need to check the cancellation token inside of your inner for. As it is now, it will check for cancel before entering, but after that it won't look at the token again.

for (int k = 0; k < 1000000000; k++)
{
    po.CancellationToken.ThrowIfCancellationRequested();
    counterSubtotal++;

    if (i == 4 && k == 900000000)
    {
       cts.Cancel();

    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top