Async void or Task.Run? - c#

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)));

Related

Stuck with async await thread

In constructor I want to call one method type :
private async Task OnLoadPrometDanKorisnikDatum
and I want to wait that method while its finish, and I have more method(3) like this and I want to call this 3 methods in background thread and don't wait him to finish, just want to wait first method. And I want to them executing parallel.
I have methods async Task,and in constructor of view model I call like this
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat,
DatumVrednost).Wait();
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).Wait();
if I don't place .Wait() on the end, program doesn't work. I see in debug mode they run asynchronly, but time spent tell me that they sub(one method time + second method time + ....).
Can someone help me, this is for me very stuf...
Answer
The best way to handle your scenario is to use async void.
I recommend first reading the Explanation section below to fully understand the best practices around async void.
public MyConstructor()
{
ExecuteAsyncMethods();
}
async void ExecuteAsyncMethods()
{
try
{
await OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost);
await OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja);
}
catch(Exception e)
{
//Handle Exception
}
}
Explanation
Many C# devs are taught "Never use async void", but this is one of the few use-cases for it.
Yes async void can be dangerous and here's why:
Cannot await an async avoid method
Can lead to race conditions
Difficult to catch an Exception thrown by async void methods
E.g. the following try/catch block will not catch the Exception thrown here:
public MyConstructor()
{
try
{
//Cannot await `async void`
AsyncVoidMethodWithException();
}
catch(Exception e)
{
//Will never catch the `Exception` thrown in `AsyncVoidMethodWithException` because `AsyncVoidMethodWithException` cannot be awaited
}
//code here will be executing by the time `AsyncVoidMethodWithException` throws the exception
}
async void AsyncVoidMethodWithException()
{
await Task.Delay(2000);
throw new Exception();
}
That being said, as long as we wrap the contents of our entire async void in a try/catch block, we will be able to catch the exception, like so:
public MyConstructor()
{
AsyncVoidMethodWithException();
}
async void AsyncVoidMethodWithException()
{
try
{
await Task.Delay(2000);
throw new Exception();
}
catch(Exception e)
{
//Exception will be caught and successfully handled
}
}
SafeFireAndForget
I created a library to help with this and its additional benefit is that it avoids writing async void code that could be potentially misused by future devs.
It's open source and also available on NuGet:
Source Code
NuGet Package
SafeFireAndForget
SafeFireAndForget allows us to safely execute a Task whilst not blocking the calling thread and without waiting for it to finish before moving to the next line of code.
Below is a simplified version of SafeFireAndForget that you can add to your project.
However, I recommend copy/pasting its complete source code or adding its NuGet Package to your library to get a more robust implementation
public static async void SafeFireAndForget<TException>(this Task task, Action<TException> onException = null, bool continueOnCapturedContext = false) where TException : Exception
{
try
{
await task.ConfigureAwait(continueOnCapturedContext);
}
catch (TException ex) when (onException != null)
{
onException(ex);
}
}
Using SafeFireAndForget
To use SafeFireAndForget, append it to your method call like so:
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget();
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget();
To handle any Exception thrown by that Task, use onException. Here's an example that prints the Exception to the Debug Console:
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget(ex => Debug.WriteLine(ex));
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget(ex => Debug.WriteLine(ex));

Cannot handle exceptions thrown in a task and continue where it left off

I have defined an extension method to capture exceptions thrown in a task and write it to log.
public static Task IgnoreExceptions(this Task task)
{
task.ContinueWith(t =>
{
var ignored = t.Exception;
Console.WriteLine("Error" + ignored.Message);
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted);
return task;
}
However, it doesn't seem to work for me. The execution does not continue where it should be. Can anyone tell what's wrong with my IgnoreExceptions?
try {
await DoSomething().IgnoreExceptions().ConfigureAwait(false);
await DoSomethingElse().IgnoreExceptions().ConfigureAwait(false);
}
catch(Exception e){
// If DoSomething() throws, the error is written to console but the code reaches here instead of continuing to call DoSomethingElse()
}
The problem of your implementation is that you're returning the original task. Return the one that is created by ContinueWith might work (I haven't tested it).
old:
task.ContinueWith(...);
return task;
new:
var result = task.ContinueWith(...);
return result;
But I would prefer an async/await approach and rewrite the extension method like that:
public static async Task IgnoreException(this Task task)
{
try
{
await task;
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
DEMO

TaskCanceledException with ContinueWith

I've been trying to figure out why I'm getting a TaskCanceledException for a bit of async code that has recently started misbehaving. I've reduced my issue down to a small code snippet that has me scratching my head:
static void Main(string[] args)
{
RunTest();
}
private static void RunTest()
{
Task.Delay(1000).ContinueWith(t => Console.WriteLine("{0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted).Wait();
}
As far as I'm aware, this should simply pause for a second and then close. The ContinueWith won't be called (this only applies to my actual use-case). However, instead I'm getting a TaskCanceledException and I've no idea where that is coming from!
You are using the wrong taskcontinuationoption:
See following link : https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskcontinuationoptions%28v=vs.110%29.aspx
It says :
Specifies that the continuation task should be scheduled only if its antecedent threw an unhandled exception. This option is not valid for multi-task continuations.
As guys said above this call requires just antecedent-task in faulted-status otherwise will throw TaskCanceledException, for this concrete case you can generalize ContinueWith to process all statuses:
await Task.Delay(1000).ContinueWith(
task =>
{
/* take into account that Canceled-task throw on next row the TaskCancelledException */
if (!task.IsFaulted) {
return;
}
Console.WriteLine("{0}", task.Exception);
// do smth like 'throw task.Exception.InnerException'
});
I also received this error:
The block of code looked like this:
private void CallMediator<TRequest>(TRequest request) where TRequest : IRequest<Unit>
{
_ = Task.Run(async () =>
{
var mediator = _serviceScopeFactory.CreateScope().ServiceProvider.GetService<IMediator>()!;
await mediator.Send(request).ContinueWith(LogException, TaskContinuationOptions.OnlyOnFaulted);
});
}
private void LogException(Task task)
{
if (task.Exception != null)
{
_logger.LogError(task.Exception, "{ErrorMessage}", task.Exception.Message);
}
}
Reading the documentation for the ContinueWith method, it has the following remarks:
The returned Task will not be scheduled for execution until the current task has completed. If the continuation criteria specified through the continuationOptions parameter are not met, the continuation task will be canceled instead of scheduled.
So for me, it called the first task (mediator.Send(request)), then it continued with the task ContinueWith(...), which is the one I awaited. However, since an exception had not occurred in the first task, the second task was cancelled. Therefore, when awaiting the second task, it threw a TaskCanceledException.
What I did, was to change the code to this:
private void CallMediator<TRequest>(TRequest request) where TRequest : IRequest<Unit>
{
_ = Task.Run(async () =>
{
var mediator = _serviceScopeFactory.CreateScope().ServiceProvider.GetService<IMediator>()!;
try
{
_ = await mediator.Send(request);
}
catch (Exception ex)
{
_logger.LogError(ex, "{ErrorMessage}", ex.Message);
}
});
}
Instead of using .ContinueWith(...), I have replaced it with just a regular try-catch block in case of the task I am interested in fails. I think this simplifies the code and makes it more readable.
In the question, there is this line of code:
Task.Delay(1000).ContinueWith(t => Console.WriteLine("{0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted).Wait();
I would rewrite it to:
try
{
Task.Delay(1000).Wait();
}
catch (Exception ex)
{
Console.WriteLine("{0}", ex);
}

Asynchronous anonymous parameterless method as a method parameter

I have a class used as a parent to all my ViewModels. It contains a specific method used to call others methods, and show loading messages and message boxes on error (mainly):
public class BaseViewModel
{
async public void Try(Func<Task> action, string errorMessage = null, string waitMessage = null)
{
try
{
if (waitMessage != null)
ShowLoading(waitMessage);
await action();
}
catch (Exception e)
{
ShowError(errorMessage, e);
}
finally
{
HideLoading();
}
}
}
It is asynchronous, so my ShowLoading can be animated and stuff like that.
Is it correctly implemented?
It will always get anonymous (lambda) parameterless Tasks. My main issue is on how to actually construct these Tasks. Let's say I have a Command in a ViewModelBase's child, which call the following method when executed:
private void OnMyCommandExecute()
{
Try(() =>
{
Thread.Sleep(5000);
}, "error", "please wait");
}
It does not compile because Not all code paths return a value in lambda expression of type 'System.Func<System.Threading.Tasks.Task>'. Obvious, since we await this Func. Which leads me to the second question:
What should I put inside my Try call in this example for it to work?
I tried some really ugly things, and I really hope the answer is way different, else it will be a pain of readability:
Try(async () =>
{
return await Task.Factory.StartNew(() =>
{
SharePointService.Connect(Connection);
IsConnected = true;
});
}
It does not compile, but at this point, it's better like that. Error on return: Since 'System.Func<System.Threading.Tasks.Task>' is anasyncmethod that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task<T>'?
Try accepts a method that returns a Task. In your first example you're providing a method that is void.
In your second example you're providing a method that returns a Task<Task>, but trying to use it in a context where a Task (non-generic) is expected.
If you want to use a non-async lambda, then just have that lambda return the Task that you want to use:
Try(()=>Task.Factory.StartNew(() =>
{
SharePointService.Connect(Connection);
IsConnected = true;
}));
If you want to use an async lambda, then you need to await the task without returning it:
Try(async () => await Task.Factory.StartNew(() =>
{
SharePointService.Connect(Connection);
IsConnected = true;
}));
Note that there's no real purpose to having an async lambda here. These two snippets will both perform identically, but the second adds some extra overhead in code bloat as well as a whole state machine that just isn't actually needed at runtime.
What should I put inside my Try call in this example for it to work?
You need to make that lambda expression async by adding (surprisingly) async:
Try(async () =>
{
Thread.Sleep(5000);
}, "error", "please wait");
However, while this will enable you to create an async delegate there's nothing actually asynchronous about it (it blocks the calling thread with Thread.Sleep). If this is just an example then:
Try(async () =>
{
await Task.Delay(5000);
}, "error", "please wait");
is a better one. If it isn't don't use async at all.
Is it correctly implemented?
Not really. async void should almost always be avoided (unless in a UI event handler). Use async Task instead and make sure to await the returned task in some point to ensure the operation completed without any exceptions.
In order for Try to be as transparent as possible, I ended up with this.
async public Task Try(Action action, string errorMessage = null, string waitMessage = null)
{
try
{
if (waitMessage != null)
{
ShowLoading(waitMessage);
await Task.Factory.StartNew(() => action());
}
else
action();
}
catch (Exception e)
{
ShowError(errorMessage, e);
}
finally
{
HideLoading();
}
}
Therefore, you don't have to work with Task.Factory.StartNew or async/await when you call it:
Try(() =>
{
Thread.Sleep(5000);
}, "error", "please wait");

Exception handling in Parallel.Invoke inside a Task

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);

Categories