I was implementing a Parallel.ForEach loop to do some work, but I bounced into a problem due to an unhandled exception, while I thought I handle the cancellation.
To try and get the finger on the problem, I made a simple test setup in a winform. It has a start button, a cancel button and a label for output.
The code:
public partial class Form1 : Form
{
CancellationTokenSource cts = new CancellationTokenSource();
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
output.Text = "Running";
try
{
var runTask = Task<string>.Factory.StartNew(() => Run());
await runTask;
this.output.Text = runTask.Result;
}
catch(Exception ex)
{
throw ex;
}
}
private string Run()
{
int useThreads = Environment.ProcessorCount - 2 < 1 ? 1 : Environment.ProcessorCount - 2;
ParallelOptions options = new ParallelOptions() { MaxDegreeOfParallelism = useThreads, CancellationToken = cts.Token };
options.CancellationToken.Register(() => ActionOnCancel());
List<int> somelist =new List<int>();
for(int i = 0; i < 100; i++)
somelist.Add(i);
Parallel.ForEach(somelist, options, (row, loopstate) =>
{
if(loopstate.ShouldExitCurrentIteration || loopstate.IsExceptional)
loopstate.Stop();
Thread.Sleep(1000);
});
return "Done";
}
private void ActionOnCancel()
{
output.Text= "Cancelled";
}
private void button2_Click(object sender, EventArgs e)
{
cts.Cancel();
}
When I run the program and hit the cancel button (to trigger button2_Click), I keep on getting this error:
An exception of type 'System.OperationCanceledException' occurred in mscorlib.dll but was not handled in user code
Additional information: The operation was canceled.
If there is a handler for this exception, the program may be safely continued.
And the debugger highlights the Parallel.ForEach section. But why??? I thought I handled cancellation correct via the CancellationToken.
The exception message in ex doesn't give me any clarity:"{"The operation was canceled."}" uh...yeah....that was the intention...
What am I overlooking? Regards,
Matthijs
This exception is thrown always. You must handle the OperationCanceledException when accessing the Parallel Tasks if the cancel method is called on the CancellationTokenSource.
try
{
Parallel.ForEach(somelist, options, (row, loopstate) =>
{
if(loopstate.ShouldExitCurrentIteration || loopstate.IsExceptional)
loopstate.Stop();
Thread.Sleep(1000);
});
}
catch (OperationCanceledException)
{
// Handle the cancelled Task
}
CancellationToken.Register() offers a way to add a callback to cancellation, but doesn't "handle" it, in terms of the OperationCanceledException.
I suggest you to place a try/catch block in the Run() method as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With