We start using CancellationToken in out app a lot, so we have to change exception handling correspondingly:
class Program
{
static async Task Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource(100);
await DoJob(cts.Token);
Console.WriteLine("Successfully finished");
}
private static async Task DoJob(CancellationToken ct)
{
try
{
await Task.Delay(1000, ct);
}
catch (Exception e) when(!(e is OperationCanceledException))
{
Console.WriteLine("Do cleanup in case of error.");
}
}
}
The idea behind this code is that if someone use catch(Exception e) (please do not blame me for this) and forgot to exclude CancellationToken, an error handling is executed, for example, there is a log that operation failed. But it is not true, id doesn't fail, it just has been canceled. And cancellation should be handled differently then failure.
It seems to me like a big boilerplate to write practically in every general catch
catch (Exception e) when(!(e is OperationCanceledException))
Is there some more robust solution with less boilerplate?
You could create a method that accepts a Func<Task> and catches the exception(s), e.g.:
class Program
{
static async Task Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource(100);
await GeneralDoJobAndCatchException(() => DoJob(cts.Token));
Console.WriteLine("Successfully finished");
}
private static async Task GeneralDoJobAndCatchException(Func<Task> func)
{
try
{
await func();
}
catch (OperationCanceledException) { }
catch (Exception e)
{
Console.WriteLine("Do error handling");
}
}
private static async Task DoJob(CancellationToken ct)
{
await Task.Delay(1000, ct);
}
}
We're having the exact same problem. Mainly there is a while-loop that checks for the CancellationToken but you've to catch this exception.
We created the following extension method:
public static async Task<TaskStatus> HideCancellationException(this Task task)
{
try
{
await task;
return task.Status;
}
catch (OperationCanceledException)
{
return TaskStatus.Canceled;
}
}
Having this extension method allows to change this code:
while (!cancellationToken.IsCancellationRequested)
{
// do stuff here...
try
{
await Task.Delay(..., cancellationToken);
}
catch (OperationCanceledException)
{
// expected
}
}
to something like that:
while (!cancellationToken.IsCancellationRequested)
{
// Do stuff here.
await Task.Delay(..., cancellationToken).HideCancellationException();
}
Keep in mind that there is explicitly no overload for Task<T> because the return value in case of cancellation is default. You can't distinguish between default as normal task result and default as result of cancellation. In that case it's better to catch the exception.
You could get rid of the try-catch block altogether by awaiting indirectly with Task.WhenAny, and then querying the status of the completed task:
private static async Task DoJob(CancellationToken ct)
{
var completedTask = await Task.WhenAny(Task.Delay(1000, ct));
if (completedTask.IsFaulted)
{
Console.WriteLine("Error: " + completedTask.Exception.InnerException);
}
else if (completedTask.IsCanceled)
{
// Do nothing
}
else // Success
{
// Do nothing
}
}
Related
I only want to use CancellationTokenSource for timeout and cancellation handling.
How to distinguish if a TaskCanceledException occured due to a timeout or due to manual cancellation?
Here is a simplified example. In the real program I neither know if CancellationTokenSource .CancelAfter() was used nor if someone called CancellationTokenSource.Cancel()
static CancellationTokenSource cts = new CancellationTokenSource();
static void Main(string[] args)
{
Task.Run(async () =>
{
try
{
await SomeClass.DoSomething(cts.Token);
}
catch (TaskCanceledException ex)
{
//How to find out if the exception occured due to timeout or a call to cts.Cancel()
}
});
while (true)
{
Thread.Sleep(100);
if (someCondition)
cts.Cancel();
}
}
public class SomeClass
{
public static async Task DoSomething(CancellationToken ct)
{
using (var innerCts = CancellationTokenSource.CreateLinkedTokenSource(ct))
{
innerCts.CancelAfter(1000);
//Simulate some operation
await Task.Delay(10000, innerCts.Token);
}
}
}
thanks
Tom
AFAIK this is the most commonly used pattern:
Task.Run(async () =>
{
try
{
await SomeClass.DoSomething(cts.Token);
}
catch (OperationCanceledException) when (cts.IsCancellationRequested)
{
// cts cancellation occurred
}
catch (OperationCanceledException)
{
// Timeout occurred
}
});
Another idea is to change the implementation of the SomeClass.DoSomething method, assuming that you are allowed to do it, so that in case of timeout it throws a TimeoutException instead of an OperationCanceledException.
public static async Task DoSomething(CancellationToken cancellationToken)
{
using var innerCts = new CancellationTokenSource(millisecondsDelay: 1000);
using var linkedCts = CancellationTokenSource
.CreateLinkedTokenSource(cancellationToken, innerCts.Token);
try
{
// Simulate some operation
await Task.Delay(10000, linkedCts.Token);
}
catch (OperationCanceledException) when (innerCts.IsCancellationRequested)
{
throw new TimeoutException();
}
}
I don't think this question is a duplicate of "Proper way to deal with exceptions in DisposeAsync".
Let's say my class that implements IAsynsDisposable because it has a long-running background task, and DisposeAsync terminates that task. A familiar pattern might be the Completion property, e.g. ChannelReader<T>.Completion (despite ChannelReader doesn't implement IAsynsDisposable).
Is it considered a good practice to propagate the Completion task's exceptions outside DisposeAsync?
Here is a complete example that can be copied/pasted into a dotnet new console project. Note await this.Completion inside DisposeAsync:
try
{
await using var service = new BackgroundService(TimeSpan.FromSeconds(2));
await Task.Delay(TimeSpan.FromSeconds(3));
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadLine();
}
class BackgroundService: IAsyncDisposable
{
public Task Completion { get; }
private CancellationTokenSource _diposalCts = new();
public BackgroundService(TimeSpan timeSpan)
{
this.Completion = Run(timeSpan);
}
public async ValueTask DisposeAsync()
{
_diposalCts.Cancel();
try
{
await this.Completion;
}
finally
{
_diposalCts.Dispose();
}
}
private async Task Run(TimeSpan timeSpan)
{
try
{
await Task.Delay(timeSpan, _diposalCts.Token);
throw new InvalidOperationException("Boo!");
}
catch (OperationCanceledException)
{
}
}
}
Alternatively, I can observe service.Completion explicitly in the client code (and ignore its exceptions inside DiposeAsync to avoid them being potentially thrown twice), like below:
try
{
await using var service = new BackgroundService(TimeSpan.FromSeconds(2));
await Task.Delay(TimeSpan.FromSeconds(3));
await service.Completion;
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadLine();
}
class BackgroundService: IAsyncDisposable
{
public Task Completion { get; }
private CancellationTokenSource _diposalCts = new();
public BackgroundService(TimeSpan timeSpan)
{
this.Completion = Run(timeSpan);
}
public async ValueTask DisposeAsync()
{
_diposalCts.Cancel();
try
{
await this.Completion;
}
catch
{
// the client should observe this.Completion
}
finally
{
_diposalCts.Dispose();
}
}
private async Task Run(TimeSpan timeSpan)
{
try
{
await Task.Delay(timeSpan, _diposalCts.Token);
throw new InvalidOperationException("Boo!");
}
catch (OperationCanceledException)
{
}
}
}
Is there a concensus about which option is better?
For now, I've settled on a reusable helper class LongRunningAsyncDisposable (here's a gist, warning: barely tested yet), which allows:
to start a background task;
stop this task (via a cancellation token) by calling IAsyncDisposable.DisposeAsync at any time, in a thread-safe, concurrency-friendly way;
configure whether DisposeAsync should re-throw the task's exceptions (DisposeAsync will await the task's completion either way, before doing a cleanup);
observe the task's status, result and exceptions at any time via LongRunningAsyncDisposable.Completion property.
This question has already been asked but I still don't get it... I'm trying to cancel a task but when I include ThrowIfCancellationRequested(), it is not catched and if I don't include it, the GUI freezes...
Here is my code that run without the ThrowIfCancellationRequested() but freeze the GUI:
public void StartProcess(double myvariable)
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
processThread = Task.Factory.StartNew(() =>
{
while (true)
{
//Do some work with myvariable
if (token.IsCancellationRequested)
{
break;
}
}
}, token);
}
And the code with the ThrowIfCancellationRequested() that is not catched, the debugger stops on the line token.ThrowIfCancellationRequested():
public void StartProcess(double myvariable)
{
tokenSource = new CancellationTokenSource();
CancellationToken tokenDispatcher = tokenSource.Token;
processThread = Task.Factory.StartNew(() =>
{
try
{
while (true)
{
//Do some work with myvariable
if (token.IsCancellationRequested)
{
token.ThrowIfCancellationRequested();
break;
}
}
}
catch (AggregateException ae)
{
if (ae.InnerException is OperationCanceledException)
{
Console.WriteLine("Process Thread Cancelled");
}
else
{
Console.WriteLine("ERROR");
}
}
}, token);
}
What am I doing wrong?
The ThrowIfCancellationRequested is the proper way to cancel the task. What you need to understand is that there will be no exception when you cancel a Task. Only the Status of the task will be set to Canceled. The try catch block you have inside the task will do nothing.
To get an exception you need to await the Task in an async method or use the Wait method to wait for the finish of the task. Using the Wait method is not recommended because you are blocking the current thread until the task finishes.
Here is a suggestion how you could implement this from your code.
public static Task StartProcess(double myvariable)
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
return Task.Factory.StartNew(() =>
{
while (true)
{
//Do some work with myvariable
token.ThrowIfCancellationRequested();
}
}, token);
}
public async Task ExecuteProcess(double myvariable)
{
try
{
await StartProcess(myvariable);
}
catch (OperationCanceledException ex)
{
Console.WriteLine("Process Thread Canceled");
}
catch (Exception ex)
{
Console.WriteLine("ERROR");
}
}
The ExecuteProcess method demonstrates how you need to call your function to get the exception. This way you also prevent the GUI from blocking if you call the method from the UI thread.
I recommend you read the documentation on asynchronous programming to get more understanding on how it works.
New to async await integration in C# 5. I'm working with some basic Task based methods to explore async await and the TPL. In this example below I'm calling a web service with a timeout of 5 seconds. If the timeout expires it should throw an exception so I can return false from the method. However, the timeout never occurs, or maybe it does but the Task never returns.
public static Task<bool> IsConnectedAsync()
{
return Task.Run(() =>
{
try
{
using (WSAppService.AppService svc = new NCSoftware.Common.WSAppService.AppService(GetServiceUrl(WebService.app)){Timeout = 5000})
{
return svc.PingB();
}
}
catch (Exception ex)
{
Logger.LogException(ex.Message, ex, "IsConnectedAsync");
}
return false;
});
}
If you could please help with how to properly handle this so that if the timeout occurs or even better, an exception occurs, the Task does return.
In general, you shouldn't use Task.Run if you're wrapping async services. Since this is a service reference, you should be able to expose an async method (returning Task) directly from the service, in which case you could use:
public async static Task<bool> IsConnectedAsync()
{
try
{
using (WSAppService.AppService svc = new NCSoftware.Common.WSAppService.AppService(GetServiceUrl(WebService.app)){Timeout = 5000})
{
return await svc.PingBAsync();
}
}
catch (Exception ex)
{
Logger.LogException(ex.Message, ex, "IsConnectedAsync");
}
return false;
}
If you must wrap via Task.Run (again, this is not suggested, as it's turning synchronous code into async via the thread pool, which is typically better handled by the user at the top level), you could do:
public async static Task<bool> IsConnectedAsync()
{
try
{
return await Task.Run(() =>
{
using (WSAppService.AppService svc = new NCSoftware.Common.WSAppService.AppService(GetServiceUrl(WebService.app)){Timeout = 5000})
{
return svc.PingB();
}
}
}
catch (Exception ex)
{
Logger.LogException(ex.Message, ex, "IsConnectedAsync");
return false;
}
}
This example "fails":
static async void Main(string[] args)
{
try
{
await TaskEx.Run(() => { throw new Exception("failure"); });
}
catch (Exception)
{
throw new Exception("success");
}
}
That is, the exception with the text "failure" bubbles up.
Then I tried this workaround:
static async void Main(string[] args)
{
try
{
await SafeRun(() => { throw new Exception("failure"); });
}
catch (Exception)
{
throw new Exception("success");
}
}
static async Task SafeRun(Action action)
{
var ex = default(Exception);
await TaskEx.Run(() =>
{
try
{
action();
}
catch (Exception _)
{
ex = _;
}
});
if (ex != default(Exception))
throw ex;
}
That didn't help either.
I suppose my Async CTP refresh installation could be hosed.
Should this code work as I expect ("success" bubbles up, not "failure"), or is this not "supposed" to work that way. And if not, how would you work around it?
The behavior you are seeing is likely an edge case bug or may even be correct, if unintuitive. Normally when you invoke an async method synchronously, it wraps a task around to execute and since there is no one waiting on the task to finish, the exception never makes it to the main thread. If you were to call Main directly it would succeed, but then your runtime would see an exception of "success" on another thread.
Since main is the entrypoint of your application, it is invoked synchronously and likely as the entrypoint doesn't trigger the Task wrapping behavior, so that await isn't run properly and the TaskEx.Run throws on its own thread, which shows up in the runtime as an exception being thrown on another thread.
If you were to run main as an async method, i.e. returning a Task (since an async that returns void can only really be called via await) and blocking on it from your synchronous main context, you would get the appropriate behavior as the below test illustrates:
static async Task Main() {
try {
await TaskEx.Run(() => { throw new Exception("failure"); });
} catch(Exception) {
throw new Exception("success");
}
}
static async Task Main2() {
await Main();
}
[Test]
public void CallViaAwait() {
var t = Main2();
try {
t.Wait();
Assert.Fail("didn't throw");
} catch(AggregateException e) {
Assert.AreEqual("success",e.InnerException.Message);
}
}
[Test]
public void CallDirectly() {
var t = Main();
try {
t.Wait();
Assert.Fail("didn't throw");
} catch(AggregateException e) {
Assert.AreEqual("success", e.InnerException.Message);
}
}
I.e. the Task faults with an AggregateException which contains the success exception as it's inner exception.