I am trying to cancel a time consuming task after a certain milliseconds and for my case, I think CancellationToken.Register method best fits compared to other approaches with constant polling or WaitHandle. CancellationToken.Register method will help me define a delegate where I plan to take the task to cancelled state and stop further execution of the task; this delegate will be invoked when the task is cancelled (certain milliseconds later as per my goal). Here is the test code I have which I intend to extend for multiple tasks and with nested tasks later:
List<Task> tasks = new List<Task>();
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = tokenSource.Token;
Task t1 = Task.Factory.StartNew(() =>
{
// check cancellation token before task begins
if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested();
// register a callback to handle cancellation token anytime it occurs
cancellationToken.Register(() =>
{
Console.WriteLine("Task t1 cancelled");
cancellationToken.ThrowIfCancellationRequested();
});
// simulating a massive task; note, it is not a repeating task
Thread.Sleep(12000);
}, cancellationToken);
tasks.Add(t1);
try
{
// cancel token after 3000 ms + wait for all tasks
tokenSource.CancelAfter(3000);
Task.WaitAll(tasks.ToArray());
// OR wait for all tasks for 3000 ms and then cancel token immediately
//Task.WaitAll(tasks.ToArray(), 3000);
//tokenSource.Cancel();
}
catch (AggregateException e)
{
Console.WriteLine("\nAggregateException thrown with the following inner exceptions:");
// Display information about each exception.
foreach (var v in e.InnerExceptions)
{
if (v is TaskCanceledException)
Console.WriteLine(" TaskCanceledException: Task {0}", ((TaskCanceledException)v).Task.Id);
else
Console.WriteLine(" Exception: {0}", v.GetType().Name);
}
Console.WriteLine();
}
finally
{
tokenSource.Dispose();
}
I am, however, facing an issue with exception handling during execution of the callback method inside cancellationToken.Register. The call to cancellationToken.ThrowIfCancellationRequested() gives me exception "OperationCanceledException was unhandled by user code" followed by "AggregateException was unhandled". I have read about VS settings with unchecking User-unhandled exception for the first OperationCanceledException exception but my application terminates after second AggregateException exception; the try..catch block for Task.WaitAll does not seem to handle this.
I tried to enclose cancellationToken.ThrowIfCancellationRequested() in a try..catch block but the problem with this approach was that the task continued with the remaining steps which I do not desire. I do not see this behaviour with polling approach.
// poll continuously to check for cancellation instead of Register
// but I do not want my massive task inside this repeating block
while (true)
{
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Task t1 Canceled.");
cancellationToken.ThrowIfCancellationRequested();
}
}
What am I doing wrong with the CancellationToken.Register approach?
The error you see is exactly because you didn't wrap the ThrowIfCancellationRequested inside a try-catch block.
In my opinion it really depends on what you're doing in place of that Sleep().
Ending the task in a cooperative way like
while(!cancellationToken.IsCancellationRequested)
{
// Do stuff
// Also check before doing something that may block or take a while
if(!cancellationToken.IsCancellationRequested)
{
Stream.Read(buffer, 0, n);
}
}
should be the best way to stop it.
If you really need to stop it no matter what, I would do wrap it in another task
Task.Run(() =>
{
// Simulate a long running task
Thread.Sleep(12*1000);
}, cancellationToken);
(tested and working)
This way you should not see any Exception Unhandled thrown.
Also, you might want to take a look at this: How do I abort/cancel TPL Tasks?
usually I encapsulated a task as follows:
Task t = Task.Factory.StartNew(() =>
{
func_can_throw_exception();
}, token).
ContinueWith
((task) =>
{
try
{
task.Wait();
}
catch (AggregateException ae)
{
ae.Handle((x) =>
{
//handle
return true;
});
}
finally
{
}
});
The questions is what happens if I wait (t.Wait();) on this task in a seperate thread (for example the GUI thread). Is this allowed. If there is an exception during task execution, how is this handled?
Don't do it like that. The better way is:
Task.Run(() => {
func_can_throw_exception();
})
.ContinueWith(task => {
do_something_with(task.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
But in the code you provided task.Wait() won't block since ContinueWith only fires after the task is finished.
In the general case, task.Wait() will block the current thread until the task is finished. If the task fails, then Wait will throw an AggregateException. But using Wait can cause deadlocks if you're not careful. It's best to use continuations in TPL code.
It's usually a bad idea to Wait on tasks since it blocks the calling thread as opposed to await which waits asynchronously. It's espically dangerous to block the GUI thread since it can very quickly lead to deadlocks.
You are handling any exception internally so unless Handle throws an exception t.Wait() would not throw any exceptions.
What you should be doing is using async-await:
try
{
await Task.Run(() => func_can_throw_exception());
}
catch (Exception e)
{
// handle exception
}
I read about the Cancellation methods but based on my understanding they all provide control over tasks from outside the task, but if i want to cancel a task from the inside.
For example in this pseudo code:
Task tasks = new Task(() =>
{
bool exists = CheckFromDB();
if (!exists)
break;
}
Can i cancel a task from the inside?
The only idea i got is to trigger an exception inside the task and handle it from outside but surly that is not the way to do.
If you want to truly Cancel the task (e.g. after it's completed the Status property would be set to Canceled), you can do it this way:
var cts = new CancellationTokenSource();
var token = cts.Token;
Task innerCancel = new Task(
() =>
{
if (!CheckFromDB())
{
cts.Cancel();
}
token.ThrowIfCancellationRequested();
},
token);
When you use return task won't be actually canceled, but rather code after the return statement won't be executed, and the task itself will have its state as RanToCompletion.
On a side note it's advised to use Task.Factory.StartNew for .NET 4 and Task.Run for .NET 4.5 instead of constructor to create tasks.
The lambda expression code inside a task behaves just the same as in any other method. If you want to end the task for some reason you can just simply return:
Task task = Task.Run(() =>
{
if(!CheckFromDB())
{
return;
}
}
You can also throw an exception. That will end the task and mark it as faulted but there's no reason to do so if you can avoid it. If you are really responding to some problem that you can't overcome, then yes, just throw an exception.
I need to run multiple async tasks in a console application, and wait for them all to complete before further processing.
There's many articles out there, but I seem to get more confused the more I read. I've read and understand the basic principles of the Task library, but I'm clearly missing a link somewhere.
I understand that it's possible to chain tasks so that they start after another completes (which is pretty much the scenario for all the articles I've read), but I want all my Tasks running at the same time, and I want to know once they're all completed.
What's the simplest implementation for a scenario like this?
Both answers didn't mention the awaitable Task.WhenAll:
var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();
await Task.WhenAll(task1, task2);
The main difference between Task.WaitAll and Task.WhenAll is that the former will block (similar to using Wait on a single task) while the latter will not and can be awaited, yielding control back to the caller until all tasks finish.
More so, exception handling differs:
Task.WaitAll:
At least one of the Task instances was canceled -or- an exception was thrown during the execution of at least one of the Task instances. If a task was canceled, the AggregateException contains an OperationCanceledException in its InnerExceptions collection.
Task.WhenAll:
If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion state before it's returned to the caller.
You could create many tasks like:
List<Task> TaskList = new List<Task>();
foreach(...)
{
var LastTask = new Task(SomeFunction);
LastTask.Start();
TaskList.Add(LastTask);
}
Task.WaitAll(TaskList.ToArray());
You can use WhenAll which will return an awaitable Task or WaitAll which has no return type and will block further code execution simular to Thread.Sleep until all tasks are completed, canceled or faulted.
WhenAll
WaitAll
Any of the supplied tasks completes in a faulted state
A task with the faulted state will be returned. The exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
An AggregateException will be thrown.
None of the supplied tasks faulted but at least one of them was canceled
The returned task will end in the TaskStatus.Canceled state
An AggregateException will be thrown which contains an OperationCanceledException in its InnerExceptions collection
An empty list was given
An ArgumentException will be thrown
The returned task will immediately transition to a TaskStatus.RanToCompletion State before it's returned to the caller.
Doesn't block the current thread
Blocks the current thread
Example
var tasks = new Task[] {
TaskOperationOne(),
TaskOperationTwo()
};
Task.WaitAll(tasks);
// or
await Task.WhenAll(tasks);
If you want to run the tasks in a particular/specific order you can get inspiration from this answer.
The best option I've seen is the following extension method:
public static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
return Task.WhenAll(sequence.Select(action));
}
Call it like this:
await sequence.ForEachAsync(item => item.SomethingAsync(blah));
Or with an async lambda:
await sequence.ForEachAsync(async item => {
var more = await GetMoreAsync(item);
await more.FrobbleAsync();
});
Yet another answer...but I usually find myself in a case, when I need to load data simultaneously and put it into variables, like:
var cats = new List<Cat>();
var dog = new Dog();
var loadDataTasks = new Task[]
{
Task.Run(async () => cats = await LoadCatsAsync()),
Task.Run(async () => dog = await LoadDogAsync())
};
try
{
await Task.WhenAll(loadDataTasks);
}
catch (Exception ex)
{
// handle exception
}
Do you want to chain the Tasks, or can they be invoked in a parallel manner?
For chaining
Just do something like
Task.Run(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
Task.Factory.StartNew(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
and don't forget to check the previous Task instance in each ContinueWith as it might be faulted.
For the parallel manner
The most simple method I came across: Parallel.Invoke
Otherwise there's Task.WaitAll or you can even use WaitHandles for doing a countdown to zero actions left (wait, there's a new class: CountdownEvent), or ...
This is how I do it with an array Func<>:
var tasks = new Func<Task>[]
{
() => myAsyncWork1(),
() => myAsyncWork2(),
() => myAsyncWork3()
};
await Task.WhenAll(tasks.Select(task => task()).ToArray()); //Async
Task.WaitAll(tasks.Select(task => task()).ToArray()); //Or use WaitAll for Sync
I prepared a piece of code to show you how to use the task for some of these scenarios.
// method to run tasks in a parallel
public async Task RunMultipleTaskParallel(Task[] tasks) {
await Task.WhenAll(tasks);
}
// methode to run task one by one
public async Task RunMultipleTaskOneByOne(Task[] tasks)
{
for (int i = 0; i < tasks.Length - 1; i++)
await tasks[i];
}
// method to run i task in parallel
public async Task RunMultipleTaskParallel(Task[] tasks, int i)
{
var countTask = tasks.Length;
var remainTasks = 0;
do
{
int toTake = (countTask < i) ? countTask : i;
var limitedTasks = tasks.Skip(remainTasks)
.Take(toTake);
remainTasks += toTake;
await RunMultipleTaskParallel(limitedTasks.ToArray());
} while (remainTasks < countTask);
}
There should be a more succinct solution than the accepted answer. It shouldn't take three steps to run multiple tasks simultaneously and get their results.
Create tasks
await Task.WhenAll(tasks)
Get task results (e.g., task1.Result)
Here's a method that cuts this down to two steps:
public async Task<Tuple<T1, T2>> WhenAllGeneric<T1, T2>(Task<T1> task1, Task<T2> task2)
{
await Task.WhenAll(task1, task2);
return Tuple.Create(task1.Result, task2.Result);
}
You can use it like this:
var taskResults = await Task.WhenAll(DoWorkAsync(), DoMoreWorkAsync());
var DoWorkResult = taskResults.Result.Item1;
var DoMoreWorkResult = taskResults.Result.Item2;
This removes the need for the temporary task variables. The problem with using this is that while it works for two tasks, you'd need to update it for three tasks, or any other number of tasks. Also it doesn't work well if one of the tasks doesn't return anything. Really, the .Net library should provide something that can do this
If you're using the async/await pattern, you can run several tasks in parallel like this:
public async Task DoSeveralThings()
{
// Start all the tasks
Task first = DoFirstThingAsync();
Task second = DoSecondThingAsync();
// Then wait for them to complete
var firstResult = await first;
var secondResult = await second;
}
I have read a lot on how to handle exceptions in TPL but don't really understand.
Lets take this example code:
var task1 = new Task(() => { throw new Exception("Throw 1"); });
var task2 = task1.ContinueWith(t => Console.WriteLine("Catch 1:{0}", t.Exception.Message),
TaskContinuationOptions.OnlyOnFaulted);
var task3 = task2.ContinueWith(t => Console.WriteLine("Continuation"));
task1.Start();
try {
task1.Wait();
}
catch (Exception ex) {
Console.WriteLine("Wait Exception: {0}", ex.Message);
}
I expected this to print
Catch 1
Continuation
But I get
Catch 1
Continuation
Wait Exception
This means that the exception is still considered unhandled when the task completes and the task finalizer will eventually tear down the application.
How do I handle the exception inside the continuation so the finalizer will not throw? At the same time I want the task to remain in the faulted state so wrapping the task in try/catch will not work.
The background is that I want to implement the async event pattern as specified here but with error handling. My complete code looks like this
public IAsyncResult Begin(AsyncCallback callback, object state, Action action) {
var task1 = new Task(action);
var task2 = task1.ContinueWith(t => HandleException(t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
if (callback != null) {
var task3 = task2.ContinueWith(t => callback(t),
TaskScheduler.FromCurrentSynchronizationContext());
var task4 = task3.ContinueWith(t => HandleException(t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
}
task1.Start();
return task;
}
You do your wait on the task that fails, and if you read the documentation on Task.Wait carefully you will see that wait will rethrow the exception in this case.
But if you wait on your task3 everything should work as expected.
Of course you should keep this in mind:
When you use the OnlyOnFaulted option, it is guaranteed that the
Exception property in the antecedent is not null. You can use that
property to catch the exception and see which exception caused the
task to fault. If you do not access the Exception property, the
exception will go unhandled. Also, if you attempt to access the Result
property of a task that has been canceled or has faulted, a new
exception will be raised.
(Reference here)
And finally yet another good source on How to handle exceptions thrown by tasks
I hope this helps.