Does try catch outside of: await Task.Run(() => make sense or just use them only inside of await?
private async void Test()
{
try
{
await Task.Run(() =>
{
try
{
DoingSomething();
}
catch (Exception ex)
{
log.Error(ex.Message);
}
});
}
catch (Exception ex)
{
log.Error(ex.Message);
}
}
If you handle Exception inside the delegate (in your case just for logging purpose), await will not raise an exception in normal circumstances. This should be fine.
private async Task Test()
{
await Task.Run(() =>
{
try
{
DoingSomething();
}
catch (Exception ex)
{
log.Error(ex.Message);
}
});
}
However, since you are awaiting the Task, most probably, there will be some DoSomethingElse in the Test method, which might be affected by the outcome of the Task - in which case it also makes sense to have a try/catch around await.
private async Task Test()
{
try
{
await Task.Run(() =>
{
try
{
DoingSomething();
}
catch (SomeSpecialException spex)
{
// it is OK to have this exception
log.Error(ex.Message);
}
});
DoSomethingElse(); // does not run when unexpected exception occurs.
}
catch (Exception ex)
{
// Here we are also running on captured SynchronizationContext
// So, can update UI to show error ....
}
}
If the delegate you pass to Task.Run raises an exception, then you can catch it outside the Task.Run when you await the returned task.
You shouldn't think of await as though it was a block. There's no such thing as "inside of await". Instead, think of await as an operator that takes a single argument (in this case, the Task returned by Task.Run). Task.Run will catch exceptions from its delegate and place them on the returned Task; await will then propagate that exception.
You can add try catch to outside code too. The compiler will execute catch section when an exception happens during the async call. Here is more details why would you need try catch around await http://msdn.microsoft.com/en-us/library/vstudio/0yd65esw.aspx look Exceptions in Async Methods
This is the behavior in .Net 6:
try{
await Task.Run(()=> & call whatever method );
}
catch { handle the exception } <-- the catch will never get hit. An unhandled exception in the Task.Run line will happen
However, this will work:
try{
await Task.Run(async ()=> & call some method );
}
catch(Exception ex){
handle the exception
}
The async before the ()=> has to be there for .Net 6
I've just confirmed this.
Related
Welp, I've this code:
public static async Task TimedSync (CancellationToken ct)
{
try {
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
await Task.Run(async () => await UP.Sincronizacao.SyncDB(true));
Xamarin.Forms.Device.StartTimer(TimeSpan.FromMinutes(1), () => {
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
Task.Run(async () => await UP.Sincronizacao.SyncDB(false));
return true;
});
} catch (OperationCanceledException) {
await Current.MainPage.DisplayAlert("Got it", "Good", "ok");
} catch (Exception e) {
await Current.MainPage.DisplayAlert("Oops", e.Message, "dismiss");
}
}
The app just crashes at this point, and on debug I find that the exception thrown by ThrowIfCancellationRequested() is unhandled.
Edit:
Ok, something really weird happened, I removed the first if(ct.IsCancellationRequested) ct.ThrowIfCancellationRequested(); and followed Peter's suggestion, the Throw inside the lambda now throws the exception, the try catch block I put on it as well didn't work, but the try catch outside the lambda caught the exception. Here's the code:
public static async Task TimedSync (CancellationToken ct)
{
try {
await Task.Run(async () => await UP.Sincronizacao.SyncDB(true));
Xamarin.Forms.Device.StartTimer(TimeSpan.FromMinutes(1), () => {
try {
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
Task.Run(async () => await UP.Sincronizacao.SyncDB(false));
return true;
} catch (OperationCanceledException) {
return false;
}
});
} catch (OperationCanceledException) {
await Current.MainPage.DisplayAlert("Got it", "Good", "ok");
} catch (Exception e) {
await Current.MainPage.DisplayAlert("Oops", e.Message, "dismiss");
}
}
It kinda works for me :)
But still would like to understand what's going on here
You are passing StartTimer a lambda that will throw a CancellationException when cancellation happens, but this exception doesn't necessarily fire inside StartTimer or the scope of TimedSync.
My guess, because I don't use Xamarin, is that the timer code running your lambda sees the exception on a separate task and promotes that to an application failure.
If you catch CancellationException in the lambda and return false this should have the desired effect of stopping the timer without propagating an exception to the Xamarin timer code.
Note that the direct call to ct.ThrowIfCancellationRequested() will be caught inside TimedSync and hit your catch block.
Just noticed strange thing: to catch exception in caller from new Task, lambda MUST be marked as async!? Is it really necessary even if delegate has no await operators at all?
try
{
//Task.Run(() => // exception is not caught!
Task.Run(async () => // unnecessary async!?!
{
throw new Exception("Exception in Task");
}).Wait();
}
catch (Exception ex)
{
res = ex.Message;
}
Why there is neccesary for async operator?
All documentation i can find tells that delegate must not return Void and Task must be awaited for exception to propogate up to caller.
Added full code:
class Program
{
static void Main(string[] args)
{
var p = new Program();
p.Run();
}
public void Run()
{
string result;
try
{
result = OnSomeEvent((s, ea) => RunSomeTask());
}
catch (Exception ex) // Try to catch unhandled exceptions here!
{
result = ex.Message;
}
Console.WriteLine(result);
Console.ReadKey();
}
// Some other Framework bult-in event (can not change signature)
public string OnSomeEvent(EventHandler e)
{
e.Invoke(null, new EventArgs());
return "OK";
}
private async Task RunSomeTask()
{
await Task.Run(async () => // do not need async here!!!
//await Task.Run(() => // caller do not catches exceptions (but must)
{
throw new Exception("Exception in Task1");
});
}
}
So the qestion is how to catche ex. without asyn keyword???
Methods that return Task - such as Task.Run or async methods - will place any exceptions on that returned Task. It's up to you to observe that exception somehow. Normally this is done with await, like this:
await Task.Run(() => { throw ... });
In your case, the problem is in this line:
result = OnSomeEvent((s, ea) => RunSomeTask());
In this code, RunSomeTask is returning a Task, and that Task is never awaited. In order to observe the exception, you should await that task.
When using async/await, exceptions are automatically unwrapped at the site of the await. When using a Task and .Wait(), any exception are wrapped when they come out of the Task, and thus getting information requires you to dig into the Task.Exception property, since they do not propagate up the call stack.
See https://dotnetfiddle.net/MmEXsT
I do not know how I should properly propagate an exception from a Task to the thread that created this task:
private void threadMT()
{
Task task;
try
{
task = new Task(() =>
{
throw new Exception("blabla");
});
task.Start();
while(!task.IsCompleted)
Thread.Sleep(500);
if (task.IsFaulted)
throw task.Exception;
}
catch (Exception ex)
{
throw ex;
}
}
When this line is reached:
throw new Exception("blabla");
the app halts saying that the exception is not handled.
Can it be propagated back to method?
Thx in advance.
The easiest way for you to propagate an exception from a Task executed on the thread-pool is to turn it to actually return a Task which you can await on:
public async Task AwaitOnTaskAsync()
{
try
{
await DoStuffWithThreadAsync();
}
catch (Exception e)
{
}
}
public Task DoStuffWithThreadAsync()
{
return Task.Run(() => { throw new Exception("blabla"); });
}
await will make sure to unwrap the exception out of the Task, allowing you to apply a try-catch on it.
Side Note - Don't use the Task constructor, instead use Task.Run to return a "hot task" (one which has already started). There's no point in creating a Task which you're actively blocking on using Thread.Sleep later on, either execute it synchronously or use async-await to asynchronously wait on the task.
I try to catch exceptions from another thread, but can't.
static void Main(string[] args)
{
try
{
Task task = new Task(Work);
task.Start();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.WriteLine();
}
public static void Work()
{
throw new NotImplementedException();
}
I write try-catch and at method too, but nothing happens.
Please,tell me how to know that exception throw?
Maybe you could show me some example code.
Your code may not raise the exception as the main method will executes too fast and the process will terminate before you got the exception
Here how it would look your code
static void Main(string[] args)
{
Task task = new Task(Work);
task.Start();
var taskErrorHandler = task.ContinueWith(task1 =>
{
var ex = task1.Exception;
Console.WriteLine(ex.InnerException.Message);
}, TaskContinuationOptions.OnlyOnFaulted);
//here you should put the readline in order to avoid the fast execution of your main thread
Console.ReadLine();
}
public static void Work()
{
throw new NotImplementedException();
}
Try to take a look at ContinueWith
The OnlyOnFaulted member of the TaskContinuationOptions enumeration
indicates that the continuation should only be executed if the
antecedent task threw an exception.
task.ContinueWith((Sender) =>
{
////This will be called when error occures
Sender.Result
}, TaskContinuationOptions.OnlyOnFaulted);
Your try/catch wouldn't work. For one reason : because you could very well have gone out of the try block before the exception is thrown, as the Task is done on another thread.
With a Task, there are two ways to get the exceptions.
The first one is to use task.Wait(); in your try block. This method will rethrow any exception thrown by the task.
Then, any exception will be handled on the calling thread in the catch block.
The second one is to use the ContinueWith method. This won't block your calling thread.
task.ContinueWith(t =>
{
// Here is your exception :
DoSomethingWithYour(t.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
Note the following will block the main thread since Wait is employed.
try
{
Task task = Task.Factory.StartNew(Work);
task.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine(ex.ToString());
}
When using tasks for large/long running workloads that I need to be able to cancel I often use a template similar to this for the action the task executes:
public void DoWork(CancellationToken cancelToken)
{
try
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
Log.Exception(ex);
throw;
}
}
The OperationCanceledException should not be logged as an error but must not be swallowed if the task is to transition into the cancelled state. Any other exceptions do not need to be dealt with beyond the scope of this method.
This always felt a bit clunky, and visual studio by default will break on the throw for OperationCanceledException (though I have 'break on User-unhandled' turned off now for OperationCanceledException because of my use of this pattern).
UPDATE: It's 2021 and C#9 gives me the syntax I always wanted:
public void DoWork(CancellationToken cancelToken)
{
try
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
catch (Exception ex) when (ex is not OperationCanceledException)
{
Log.Exception(ex);
throw;
}
}
Ideally I think I'd like to be able to do something like this:
public void DoWork(CancellationToken cancelToken)
{
try
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
catch (Exception ex) exclude (OperationCanceledException)
{
Log.Exception(ex);
throw;
}
}
i.e. have some sort of exclusion list applied to the catch but without language support that is not currently possible (#eric-lippert: c# vNext feature :)).
Another way would be through a continuation:
public void StartWork()
{
Task.Factory.StartNew(() => DoWork(cancellationSource.Token), cancellationSource.Token)
.ContinueWith(t => Log.Exception(t.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
}
public void DoWork(CancellationToken cancelToken)
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
but I don't really like that as the exception technically could have more than a single inner exception and you don't have as much context while logging the exception as you would in the first example (if I was doing more than just logging it).
I understand this is a bit of a question of style, but wondering if anyone has any better suggestions?
Do I just have to stick with example 1?
So, what's the problem? Just throw away catch (OperationCanceledException) block, and set proper continuations:
var cts = new CancellationTokenSource();
var task = Task.Factory.StartNew(() =>
{
var i = 0;
try
{
while (true)
{
Thread.Sleep(1000);
cts.Token.ThrowIfCancellationRequested();
i++;
if (i > 5)
throw new InvalidOperationException();
}
}
catch
{
Console.WriteLine("i = {0}", i);
throw;
}
}, cts.Token);
task.ContinueWith(t =>
Console.WriteLine("{0} with {1}: {2}",
t.Status,
t.Exception.InnerExceptions[0].GetType(),
t.Exception.InnerExceptions[0].Message
),
TaskContinuationOptions.OnlyOnFaulted);
task.ContinueWith(t =>
Console.WriteLine(t.Status),
TaskContinuationOptions.OnlyOnCanceled);
Console.ReadLine();
cts.Cancel();
Console.ReadLine();
TPL distinguishes cancellation and fault. Hence, cancellation (i.e. throwing OperationCancelledException within task body) is not a fault.
The main point: do not handle exceptions within task body without re-throwing them.
Here is how you elegantly handle Task cancellation:
Handling "fire-and-forget" Tasks
var cts = new CancellationTokenSource( 5000 ); // auto-cancel in 5 sec.
Task.Run( () => {
cts.Token.ThrowIfCancellationRequested();
// do background work
cts.Token.ThrowIfCancellationRequested();
// more work
}, cts.Token ).ContinueWith( task => {
if ( !task.IsCanceled && task.IsFaulted ) // suppress cancel exception
Logger.Log( task.Exception ); // log others
} );
Handling await Task completion / cancellation
var cts = new CancellationTokenSource( 5000 ); // auto-cancel in 5 sec.
var taskToCancel = Task.Delay( 10000, cts.Token );
// do work
try { await taskToCancel; } // await cancellation
catch ( OperationCanceledException ) {} // suppress cancel exception, re-throw others
You could do something like this:
public void DoWork(CancellationToken cancelToken)
{
try
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
catch (OperationCanceledException) when (cancelToken.IsCancellationRequested)
{
throw;
}
catch (Exception ex)
{
Log.Exception(ex);
throw;
}
}
C# 6.0 has a solution for this..Filtering exception
int denom;
try
{
denom = 0;
int x = 5 / denom;
}
// Catch /0 on all days but Saturday
catch (DivideByZeroException xx) when (DateTime.Now.DayOfWeek != DayOfWeek.Saturday)
{
Console.WriteLine(xx);
}
According to this MSDN blog post, you should catch OperationCanceledException, e.g.
async Task UserSubmitClickAsync(CancellationToken cancellationToken)
{
try
{
await SendResultAsync(cancellationToken);
}
catch (OperationCanceledException) // includes TaskCanceledException
{
MessageBox.Show(“Your submission was canceled.”);
}
}
If your cancelable method is in between other cancelable operations, you may need to perform clean up when canceled. When doing so, you can use the above catch block, but be sure to rethrow properly:
async Task SendResultAsync(CancellationToken cancellationToken)
{
try
{
await httpClient.SendAsync(form, cancellationToken);
}
catch (OperationCanceledException)
{
// perform your cleanup
form.Dispose();
// rethrow exception so caller knows you’ve canceled.
// DON’T “throw ex;” because that stomps on
// the Exception.StackTrace property.
throw;
}
}
I am not entirely sure of what you are trying to achieve here but I think the following pattern might help
public void DoWork(CancellationToken cancelToken)
{
try
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
catch (OperationCanceledException) {}
catch (Exception ex)
{
Log.Exception(ex);
}
}
You might have observed that I have removed the throw statement from here. This will not throw the exception but will simply ignore it.
Let me know if you intend to do something else.
There is yet another way which is quite close to what you have exhibited in your code
catch (Exception ex)
{
if (!ex.GetType().Equals(<Type of Exception you don't want to raise>)
{
Log.Exception(ex);
}
}