Exception handling in Parallel.Invoke inside a Task - c#

I have the following code
var exceptions = new ConcurrentQueue<Exception>();
Task task = Task.Factory.StartNew(() =>
{
try
{
Parallel.Invoke(
async () => await _aViewModel.LoadData(_someId),
async () => await _bViewModel.LoadData(_someId)
);
}
catch (Exception ex)
{
exceptions.Enqueue(ex);
}
}).ContinueWith((continuation) =>
{
if (exceptions.Count > 0) throw new AggregateException(exceptions);
});
I am using Task.StartNew here because the LoadData method use the Dispatcher.StartAsync method to invoke on the main UI thread internally.
The problem I have is that if I force _aViewModel.LoadData to throw an exception it is not caught in the Catch(Exception) clause (nor if I catch AggregateException). I don't understand why!?

Parallel.Invoke is not async-aware. So your async lambdas are being converted to async void methods, which have extremely awkward error semantics (they are not allowed to leave the async void method; instead, they are captured and re-raised directly on the SynchronizationContext that was active at the time the async void method started - in this case, the thread pool).
I'm not sure why you have the Parallel.Invoke in the first place. Since your method is already async, you could just do something like this:
Task task = Task.Factory.StartNew(async () =>
{
try
{
Task.WaitAll(
_aViewModel.LoadData(_someId),
_bViewModel.LoadData(_someId)
);
}
catch (Exception ex)
{
exceptions.Enqueue(ex);
}
})...
P.S. If you have the time, rethink the structure of this whole part of the code. Dispatcher.StartAsync is a code smell. The UI should be (asynchronously) requesting data; the data retrieval objects should not have to know about the UI.

Parallel.Invoke takes an array of Action delegates. It has no means of knowing that your delegates are actually async methods, and therefore it returns before your tasks have completed.
For an in-depth explanation of this behaviour, watch Lucian Wischik's Channel 9 video on the subject.
Try changing your code to use the Task.WhenAll method instead.
var aTask = _aViewModel.LoadData(_someId);
var bTask = _bViewModel.LoadData(_someId);
await Task.WhenAll(aTask, bTask);

Related

WhenAny behaving like WhenAll in certain case

So I had a problem with a third party library where the call could get stuck and never return even when calling cancellationToken.Cancel. The below is a prototype that take care of this situation and that it works.
public async Task MainAsync()
{
try
{
await StartAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine("Exception thrown");
}
}
private async Task<string> StartAsync()
{
var cts = new CancellationTokenSource();
cts.CancelAfter(3 * 1000);
var tcs = new TaskCompletionSource<string>();
cts.Token.Register(() => { Console.WriteLine("Cancelled"); tcs.TrySetCanceled(); });
return await (await Task.WhenAny(tcs.Task, LongComputationAsync())
.ConfigureAwait(false)).ConfigureAwait(false);
}
private async Task<string> LongComputationAsync()
{
await Task.Delay(1 * 60 * 1000).ConfigureAwait(false);
return "Done";
}
So the Above will wait 3 seconds, and it will throw a TaskCancelledException like it should.
If you then change the method LongComputationAsync to the following:
private Task<string> LongComputationAsync()
{
Task.Delay(1 * 60 * 1000).ConfigureAwait(false).GetAwaiter().GetResult();
return Task.FromResult("Done");
}
I would still expect this to have the same behaviour, but what this does is that, it will wait the full 1 minute (specified in the LongComputationAsync()) then throw the TaskCancelledException.
Can anyone explain this to me? On how this is working, or if this is the correct behaviour to begin with.
Can anyone explain this to me?
Sure. The problem doesn't have anything to do with WhenAny. Rather, the problem is that the code assumes a method is asynchronous when it's synchronous.
This is a relatively easy mistake to make. But as a general rule, a method with an asynchronous signature may be asynchronous; it does not have to be asynchronous.
As I describe on my blog, asynchronous methods begin executing synchronously, just like synchronous methods. It is only when they hit an await that they may run asynchronously (and even then, they may continue synchronously).
So, the new version of LongCompuationAsync is synchronous, and it executes the entire method before returning the task to StartAsync, which then passes it to WhenAny.

Rethrowing exceptions in tasks c# without wait

I have a GUI application, in which I want to run something in a task, so it will not hold the UI. I want un unhandled exception in the task to be propogated to the application level exception handler.
However:
If I just throw an exception in the task it will not reach app level
exceptions unless I use wait/await
Async/Await - I call the method from a UI constructor, so I can't use async/await there, since I need to continue with the consturction. I just want to run the task and forget.
I was thinking about using dispatcher.invoke, what do you think?
public MainWindow()
{
InitializeComponent();
MyMethodAsync();
InitializeA();
IntiializeB();
}
private void MyMethodAsync()
{
Task t = Task.Run(() =>
{
//Do some stuff
throw new Exception("Throwing some unexpected exception");
}).ContinueWith(MyContinueWith);
}
private void MyContinueWith(Task task)
{
if (task.IsFaulted && task.Exception != null)
{
dispatcher.BeginInvoke(new Action(() =>
{
throw task.Exception;
}), null);
}
}
Two ways I can think of. First, is register to TaskScheduler.UnobservedTaskException event and log whatever you need there:
private void MyMethodAsync()
{
// Note you should probably register only once, so this may not fit here.
TaskScheduler.UnobservedTaskException += (s, e) => GlobalLogger.Log(e);
Task t = Task.Run(() =>
{
// Do some staff
}).ContinueWith(MyContinueWith);
}
The better option which for some reason you don't want to use, is to actually await the operation and wrap it in a try-catch:
private async Task MyMethodAsync()
{
try
{
await Task.Run(() =>
{
// Do some staff
});
InvokeContinuation();
}
catch (Exception e)
{
// Log.
}
}
Do realize that by calling Task.Run you are generally spawning a new thread which is not likely what you want most of the time. Creating new threads makes sense in some instances where you are doing CPU bound work and in those cases you'll want to consider leveraging other Parallel computation libraries to get the most out of it. Instead if your work is I/O bound you should be able to use asynchronous calls all the way down.
In order to wait for the result of a async method call or an exception bubbled up to the call point you can always tack on a call to ContinueWith to the a task that is returned by the async method. If you are handling both the result and any possible exceptions then async/await semantics work nice. Note however that the code that executes in these continuations may not execute in the same thread as the original thread by default.

Async void or Task.Run?

Consider this method:
public Status SendMessage(InParam inParam)
{
try
{
Task.Run(() => MethodAsync(inParam));
return Status.Success;
}
catch (Exception ex)
{
// Log the exception
return Status.Failed;
}
}
The MethodAsync method:
public async Task<Status> MethodAsync(InParam inParam)
{
try
{
return await processor.Process(inParam);
}
catch (Exception ex)
{
// Log exception
return Status.Failed;
}
}
and the Process method:
public async Task<Status> Process(InParam inParam)
{
try
{
IMessage endpoint = (IMessage)Activator
.CreateInstance(Type.GetType(_message.AgentDLLName), args);
_messageDispatchers.Add(endpoint);
foreach (IMessage dispatcher in _messageDispatchers)
{
await Task.Run(() => dispatcher.SendMessage(_message));
}
return await Task.Run(() => Status.Success);
}
catch (Exception ex)
{
// Log exception
return Status.Failed;
}
}
So the use case is that at the end of some processing workflow an email has to be sent.
This email sending part was taking some time and the user had to wait, so the original developer put this code.
I am trying to make SendMessage call MethodAsync and should not wait for it to return.
I have read that in an async workflow the complete stack needs to be async for it to work properly. The SendMessage is not marked async.
Is this the correct way to call MethodAsync since Task.Run returns an awaitable?
As MethodAsync is marked as async the call Task.Run(() => MethodAsync(inParam)); does not make much sense.
If you want to implement it as a "fire-and-forget"-call (BAD by the way) you can simply call MethodAsync(inParam);, because this also starts the awaited method inside MethodAsync "in its own task" (simplified) and returns that. If you then do not "wait that awaitable" your code inside SendMessage will continue to execute while it is still running.
BUT as already said: "fire-and-forget" is bad design in almost all cases. Can you explain your use-case a little more, so we may can provide a better approach?
UPDATE:
If there is REALLY no way to either make SendMessage async or have a synchronous counterpart of MethodAsync I recommend the following:
public Status SendMessage(InParam inParam)
{
try
{
return AsyncPump.Run(() => MethodAsync(inParam));
}
catch (Exception ex)
{
// Log the exception
return Status.Failed;
}
}
Using the AsyncPump you can return "the real result" and have no deadlocking problems.
In your example implementation of SendMessage the try/catch also makes less sense, as the method will very likely return way before any exeption will happen inside MethodAsync.
UPDATE 2 (after updated question):
I would recommend "going async all the way". Meaning also make SendMessage async (and all methods up to the UI) so you can await the "real result", but do not lock the UI while you are waiting...
UPDATE 3:
I would also change
foreach (IMessage dispatcher in _messageDispatchers)
{
await Task.Run(() => dispatcher.SendMessage(_message));
}
to
await Task.Run(() =>
{
foreach (IMessage dispatcher in _messageDispatchers)
dispatcher.SendMessage(_message);
});
This casues less context-switches. Or even:
await Task.Run(() => Parallel.ForEach(_messageDispatchers, d => d.SendMessage(_message)));

How to try..catch ThreadPool.QueueUserWorkItem code

I have bunch of Asynchronous commands. I want to write try..catch without much of repeating. Example:
_fooCommand = new AsynchronousCommand( () => { action } );
_barCommand = new AsynchronousCommand( () => { action } );
AsynchronousCommand is class that invokes Action using ThreadPool.QueueUserWorkItem( (state) => { action() } );.
Try..catch works well when is inside lambda:
_fooCommand = new AsynchronousCommand( () => { try.exception.catch } );
When outside then not:
try
_fooCommand = new AsynchronousCommand( () => {...} );
catch
Exception is not catched.
Edit
I want to catch Exception not when creating command: when executing it using command.DoExecute(this) and if possible put try..catch inside lambda.
Exceptions propagate up the call stack on the thread on which they are thrown. Because the commands run on a thread pool thread, it will be on a different thread to your try ... catch hence it doesn't get caught.
EDIT: Assuming you do actually invoke the command within the try ... catch
You can get these semantics through the use of await. It when you await something it will schedule the remainder of the method as a continuation of the previous method, meaning that the operation is performed asynchronously. However, when the awaited operation finishes, if it represents something that throws an exception, that exception will be caught and then re-thrown within the context of your next continuation, allowing you to wrap a series of asynchronous operations in a single try/catch, having the syntax and semantics you desire. A simple example might look like:
public static async Task Foo()
{
try
{
await Task.Run(() => DoSomething());
await Task.Run(() => DoSomethingElse());
}
catch(Exception e)
{
Console.WriteLine(e);
}
}
Here DoSomething and DoSomethingElse will be run in a thread pool thread, and if either throws an exception when running, not when being started, then the catch block will be hit.

Safe to run async delegate in synchronous method on UI?

If I have an application with a synchronous method, is it safe to call an async method as shown below on a UI thread or is there an issue or potential deadlock situation? I know that calling Wait will obviously cause issues, but I feel like this may work out alright.
public void MyMainMethod(){
var getResult = Task.Run(async () => { await getResultAsync(); }).Result;
myLabel.Text = getResult;
}
I can successfully run on a UI thread without issue, but I feel as if I may be missing something. I understand that I could use a Task and ContinueWith, but in this example, I would want to wait for the result of the async method before exiting the synchronous method.
Update / Clarification
In the example above, let's assume that the MyMainMethod is an overridden method or a property, etc. and cannot be modified to be async.
Let's look at your code:
public void MyMainMethod(){
var getResult = Task.Run(async () => { await getResultAsync(); }).Result;
myLabel.Text = getResult;
}
Regardless of what's taking place inside getResultAsync, this code is blocking the UI thread when it calls task.Result. In most cases, this is already wrong.
Further, the fact that your getResultAsync is async suggests there's already an async operation inside it. There is no reason to wrap it with Task.Run, unless you perform a mix of CPU- and IO- bound tasks inside getResultAsync. Even then, it may not be necessary (see this for more details).
You can control the await continuation context inside getResultAsync with ConfiureAwait(false), and should do so to avoid deadlocks and redundant context switches, where possible.
So, the code can be reduced to:
public void MyMainMethod(){
var getResult = getResultAsync().Result;
myLabel.Text = getResult;
}
As is, it still blocks the UI. To avoid blocking, you need to make it async. See Async All the Way from Best Practices in Asynchronous Programming by Stephen Cleary.
If it cannot be modified to be async (as clarified in the update to your question), then the above is the best you can get. Indeed, it still may cause a deadlock, depending on what's going on inside getResultAsync, with out without Task.Run. To avoid deadlocks, you should not attempt to access the UI thread with a synchronous call like control.Invoke inside getResultAsync, or await any tasks scheduled on the UI thread with TaskScheduler.FromCurrentSynchronizationContext.
However, usually it is possible and desirable to re-factor the code like this into an async version:
public async Task MyMainMethod(){
var getResult = await getResultAsync();
myLabel.Text = getResult;
}
You would be calling it from a top-level entry point of your app, like a UI event handler:
async void Button_Click(object sender, EventArg e)
{
try
{
await MyMainMethod();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
it better to call your ui update through dispatcher.
Task task = LoadTask();
task.ContinueWith(t =>
Dispatcher.BeginInvoke(() => UpdateUI()));
public async Task LoadTask()
{
Task getdata =
Task.Factory.StartNew(() =>
{
Sleep(3000);
});
await getdata;
return;
}

Categories