Практическое руководство. Отмена цикла Parallel.For или Parallel.ForEach
Метод Parallel.For и Parallel.ForEach поддерживают отмену с применением маркеров отмены. Дополнительные сведения о механизмах отмены в целом см. в этой статье. В параллельном цикле CancellationToken передается в методу через параметр ParallelOptions, и этот параллельный вызов заключен в блок try-catch.
Пример
Следующий пример демонстрирует, как отменить вызов Parallel.ForEach. Этот же подход вы можете применить и к вызову Parallel.For.
namespace CancelParallelLoops
{
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main()
{
int[] nums = Enumerable.Range(0, 10_000_000).ToArray();
CancellationTokenSource cts = new();
// Use ParallelOptions instance to store the CancellationToken
ParallelOptions options = new()
{
CancellationToken = cts.Token,
MaxDegreeOfParallelism = Environment.ProcessorCount
};
Console.WriteLine("Press any key to start. Press 'c' to cancel.");
Console.ReadKey();
// Run a task so that we can cancel from another thread.
Task.Factory.StartNew(() =>
{
if (Console.ReadKey().KeyChar is 'c')
cts.Cancel();
Console.WriteLine("press any key to exit");
});
try
{
Parallel.ForEach(nums, options, (num) =>
{
double d = Math.Sqrt(num);
Console.WriteLine("{0} on {1}", d, Environment.CurrentManagedThreadId);
});
}
catch (OperationCanceledException e)
{
Console.WriteLine(e.Message);
}
finally
{
cts.Dispose();
}
Console.ReadKey();
}
}
}
' How to: Cancel a Parallel.For or ForEach Loop
Imports System.Threading
Imports System.Threading.Tasks
Module CancelParallelLoops
Sub Main()
Dim nums() As Integer = Enumerable.Range(0, 10000000).ToArray()
Dim cts As New CancellationTokenSource
' Use ParallelOptions instance to store the CancellationToken
Dim po As New ParallelOptions
po.CancellationToken = cts.Token
po.MaxDegreeOfParallelism = System.Environment.ProcessorCount
Console.WriteLine("Press any key to start. Press 'c' to cancel.")
Console.ReadKey()
' Run a task so that we can cancel from another thread.
Dim t As Task = Task.Factory.StartNew(Sub()
If Console.ReadKey().KeyChar = "c"c Then
cts.Cancel()
End If
Console.WriteLine(vbCrLf & "Press any key to exit.")
End Sub)
Try
' The error "Exception is unhandled by user code" will appear if "Just My Code"
' is enabled. This error is benign. You can press F5 to continue, or disable Just My Code.
Parallel.ForEach(nums, po, Sub(num)
Dim d As Double = Math.Sqrt(num)
Console.CursorLeft = 0
Console.Write("{0:##.##} on {1}", d, Thread.CurrentThread.ManagedThreadId)
End Sub)
Catch e As OperationCanceledException
Console.WriteLine(e.Message)
Finally
cts.Dispose()
End Try
Console.ReadKey()
End Sub
End Module
Если маркер, сообщающий об отмене, совпадает с указанным в экземпляре ParallelOptionsмаркером, то параллельный цикл создаст для отмены одно исключение OperationCanceledException. Это немедленно останавливает выполнение всех итераций при возникновении исключения. Если какой-то другой маркер вызывает отмену, цикл вызовет AggregateException исключение с как aInnerException
OperationCanceledException.