I'm trying to understand how Task.Run + Wait() + async + await work.
I have read this page: Understanding the use of Task.Run + Wait() + async + await used in one line but don't understand it quite well.
In my code, I receive events from Microsoft EventHub and process them using a class that implements IEventProcessor
.
I call DoAnotherWork()
method, which is an async
method, in ConvertToEntity()
, which is a sync method.
Since the method is async
, I use Task.Run()
and async
to delegate. (i.e. Task.Run(async () => entities = await DoAnotherWork(entities)).Wait()
)
The code has been working for a while but now my team member removed Task.Run()
and changed it to DoAnotherWork(entities).Result;
. I'm not sure this won't cause deadlocks.
I haven't asked him why he changed it but this change got me thinking "Is my code fine? Why?".
My questions are:
* What is the difference between the two?
* Which code is appropriate and/or safe (= won't cause a deadlock)?
* In what situation is a deadlock caused if it is?
* Why Task.Run()
resolve deadlock I had? (see below for detail)
Note: I'm using .NET Core 3.1.
Why I Use Task.Run()
My team had deadlock issues a few times when we used AbcAsync().Result
or .Wait()
(the method was called in a NET Core Web API methods and deadlocks occurred mostly when we ran unit tests that execute the method), so we've used Task.Run(async () => await AbcAsync()).Wait()/Result
and we haven't seen any deadlock issues since then.
However, this page: https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d says that the delagation will cause a deadloock in certain conditions.
public class EventProcessor : IEventProcessor
{
public async Task ProcessEventsAsync(PartitionContext context, IEnumerable<EventData> messages)
{
...
var result = await eventHandler.ProcessAsync(messages);
...
}
}
public Task async ProcessAsync(IEnumerable<EventData> messages)
{
...
var entities = ConvertToEntity(messages);
...
}
public List<Entity> ConvertToEntity(IEnumerable<EventData> messages)
{
var serializedMessages = Serialize(messages);
var entities = autoMapper.Map<Entity[]>(serializedMessages);
// Task.Run(async () => entities = await DoAnotherWork(entities)).Wait(); // before change
entities = DoAnotherWork(entities).Result; // after change
return entities;
}
public Task async Entity[] DoAnotherWork(Entity[] entities)
{
// Do stuff async
await DoMoreStuff(entities)...
}