Reputation: 31
I have a task that in case of an exception cancels the tasks that are using the sme cancelation token and throws an exception:
var cancellationTokenSource = new CancellationTokenSource();
var task1 = Task.Factory.StartNew(
() =>
{
try
{
// Do work
}
catch (Exception exception)
{
cancellationTokenSource.Cancel();
// rethrow the error
throw;
}
},
cancellationTokenSource.Token,
TaskCreationOptions.None,
taskScheduler);
I have another task3 (return task) that is continuation on task1 and task2 and uses the same canceleation token:
return task3 =
Task.Factory.ContinueWhenAll(
new[] { task1, task2 },
tasks =>
{
// Do work
},
cancellationTokenSource.Token,
TaskContinuationOptions.None,
this.taskScheduler).Unwrap();
Once task1 is cancelled, all task with the same cancellation token are cancelled as well. I need to get the inner exception that task1 throws, how it can be done?
Upvotes: 1
Views: 1573
Reputation: 1186
How about using async/await and and regular exception handling? It's gonna need .NET 4.5 though. Something like this (I don't know how the tasks must interact with each other, so your code might end up looking quite differently):
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
new MyClass().RunAsyncMethods();
Console.ReadLine();
}
public class MyClass
{
public async void RunAsyncMethods()
{
try
{
var cancellationTokenSource = new CancellationTokenSource();
var task1 = RunFirstTaskAsync(cancellationTokenSource);
var task2 = RunSecondTaskAsync(cancellationTokenSource);
await Task.WhenAll(task1, task2);
await RunThirdTaskAsync(cancellationTokenSource);
Console.WriteLine("Done");
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
}
}
public Task RunFirstTaskAsync(CancellationTokenSource cancelSource)
{
return Task.Run(() =>
{
try
{
Console.WriteLine("First Task is Running");
throw new Exception("Error happened in first task");
}
catch (Exception exception)
{
cancelSource.Cancel();
throw;
}
},
cancelSource.Token);
}
public Task RunSecondTaskAsync(CancellationTokenSource cancelSource)
{
return Task.Run(
() =>
{
Console.WriteLine("Second Task is Running");
},
cancelSource.Token);
}
public Task RunThirdTaskAsync(CancellationTokenSource cancelSource)
{
return Task.Run(
() =>
{
Console.WriteLine("Third Task is Running");
},
cancelSource.Token);
}
}
}
}
UPDATE: I updated the code to use WhenAll.
UPDATE: Updated the code to create tasks with the Task.Run method instead of using cold tasks.
UPDATE: You can omit the async/await (although I think it's better that way) and do some old-school TPL error handling. Simply not use a cancellation token for task3 (the return task), only for the tasks it's waiting for. When they finish (either normally, or through exception/cancellation) you can handle the exceptions in task3 like this:
// don't use the cancellation token for the third task as you used for the previous ones
var task3 = Task.Factory.ContinueWhenAll(
new[] { task1, task2 },
tasks =>
{
if (tasks[0].Exception != null)
{
tasks[0].Exception.Handle(exc =>
{
Console.WriteLine("First task failed :(");
return false; // signal that exception was handled, so it won't propagate
});
// add additional code here, or inside the Handle method above
}
if (tasks[1].Exception != null)
{
tasks[1].Exception.Handle(exc =>
{
Console.WriteLine("Second task failed :(");
return false; // signal that exception was handled, so it won't propagate
});
// add additional code here, or inside the Handle method above
}
// do the same for the rest of the tasks or iterate throught them with a foreach loop...
});
Upvotes: 2