rubiktubik
rubiktubik

Reputation: 1001

Exception is not caught at Cancelation of Task.Run

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

Answers (3)

Jcl
Jcl

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

paparazzo
paparazzo

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

Lucas Trzesniewski
Lucas Trzesniewski

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

Related Questions