【发布时间】:2020-05-04 11:39:03
【问题描述】:
我正在尝试了解 Task.Run + Wait() + async + await 的工作原理。
我已阅读此页面:Understanding the use of Task.Run + Wait() + async + await used in one line 但不太了解。
在我的代码中,我从 Microsoft EventHub 接收事件并使用实现 IEventProcessor 的类来处理它们。
我在ConvertToEntity()中调用DoAnotherWork()方法,这是一个async方法,这是一个同步方法。
由于方法是async,所以我使用Task.Run()和async进行委托。 (即Task.Run(async () => entities = await DoAnotherWork(entities)).Wait())
该代码已经运行了一段时间,但现在我的团队成员删除了Task.Run() 并将其更改为DoAnotherWork(entities).Result;。我不确定这会不会导致死锁。
我没有问他为什么要更改它,但这个更改让我思考“我的代码好吗?为什么?”。
我的问题是:
* 两者有什么区别?
* 哪个代码合适和/或安全(= 不会导致死锁)?
* 如果是,在什么情况下会导致死锁?
* 为什么Task.Run() 解决了我的死锁? (详情见下文)
注意:我使用的是 .NET Core 3.1。
我为什么使用 Task.Run()
我的团队在使用 AbcAsync().Result 或 .Wait() 时遇到过几次死锁问题(该方法在 NET Core Web API 方法中调用,死锁主要发生在我们运行执行该方法的单元测试时),所以我们使用Task.Run(async () => await AbcAsync()).Wait()/Result 从那时起我们没有看到任何死锁问题。
但是,此页面:https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d 表示,在某些情况下,延迟会导致死锁。
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)...
}
【问题讨论】:
-
另外,顺便说一下,死锁通常发生在两个线程最终等待同一资源或彼此等待,而两个线程之一永远不会完成或向前移动时。
-
Joe Albahari 出色且免费的eBook on Threading in C# 非常详细地解释了所有这些。
标签: c# asynchronous task