Reputation: 1001
I have a class Worker
which is doing some work (with simulated workload):
public class Worker
{ ...
public void DoWork(CancellationToken ct)
{
for (int i = 0; i < 10; i++)
{
ct.ThrowIfCancellationRequested();
Thread.Sleep(2000);
}
}
Now I want to use this method in a Task.Run
(from my Windows Forms App,at button-click) which can be cancelled:
private CancellationTokenSource _ctSource;
try
{
Task.Run(() =>
{
_worker.DoWork(_ctSource.Token);
},_ctSource.Token);
}
catch (AggregateException aex)
{
String g = aex.Message;
}
catch (OperationCanceledException ex)
{
String g = ex.Message;
}
catch (Exception ex)
{
String g = ex.Message;
}
But when the task is started, I can't cancel it with _ctSource.Cancel()
;
I get an error in visual studio that the OperationCanceledException
is not handled!
But I surrounded the Task.Run Call in a try-catch-clause! The Exception which ocurrs in the Worker
object should thrown up or not?
What is the problem?
Upvotes: 0
Views: 2334
Reputation: 28272
If a parallel Task throws an exception it'll return execution and will have it's Exception
property (as an AggregateException
, you should check for its InnerException
) set (and either its IsCanceled
or IsFaulted
property set to true
). Some minimal sample code from a project of mine which escalates the exception to the main thread:
var t = new Task(Initialize);
t.Start();
while (!t.IsCompleted && !t.IsFaulted)
{
// Do other work in the main thread
}
if (t.IsFaulted)
{
if (t.Exception != null)
{
if(t.Exception.InnerException != null)
throw t.Exception.InnerException;
}
throw new InvalidAsynchronousStateException("Initialization failed for an unknown reason");
}
If you use a CancellationTokenSource
it should be easy to enhance this to check for IsCanceled
(instead of IsFaulted
)
You can also use Task.Wait()
instead of the while
loop... in my project and in that precise case it seemed more appropiate to use the while loop, but you need to wait for the Task
to end in one way or another.
If you use Task.Run()
you can use a .ContinueWith(Task)
which will have the original task passed in (where you can check for IsFaulted
or IsCanceled
), or have it run only on faulted execution, at your will.
Upvotes: 0
Reputation: 45096
You need to new the token
private CancellationTokenSource _ctSource = new CancellationTokenSource();
Why are throwing an expectation in DoWork?
Exception from one thread don't bubble up another thread that started the thread.
Cancellation in Managed Threads
Upvotes: 0
Reputation: 51330
Your Task.Run
call creates the task and then returns immediately. It doesn't ever throw. But the task it creates may fail or be canceled later on.
You have several solutions here:
Use await
:
await Task.Run(...)
Attach a continuation depending on the failure/cancellation case:
var task = Task.Run(...);
task.ContinueWith(t => ..., TaskContinuationOptions.OnlyOnCanceled);
task.ContinueWith(t => ..., TaskContinuationOptions.OnlyOnFaulted);
Attach a single continuation on failure:
Task.Run(...).ContinueWith(t => ..., TaskContinuationOptions.NotOnRanToCompletion);
The solution you can/should use depends on the surrounding code.
Upvotes: 4