C# 试图理解任务。运行+;异步+;等待()/结果
我试图理解Task.Run+Wait()+async+Wait是如何工作的。C# 试图理解任务。运行+;异步+;等待()/结果,c#,asynchronous,task,C#,Asynchronous,Task,我试图理解Task.Run+Wait()+async+Wait是如何工作的。 我读过这一页,但不太明白 在我的代码中,我从Microsoft EventHub接收事件,并使用实现IEventProcessor的类处理它们。 我在ConvertToEntity()中调用DoAnotherWork()方法,这是一种async方法,它是一种同步方法。 由于方法是async,因此我使用Task.Run()和async来委托。(即Task.Run(异步()=>entities=Wait-DoAnother
我读过这一页,但不太明白 在我的代码中,我从Microsoft EventHub接收事件,并使用实现
IEventProcessor
的类处理它们。
我在ConvertToEntity()
中调用DoAnotherWork()
方法,这是一种async
方法,它是一种同步方法。
由于方法是async
,因此我使用Task.Run()
和async
来委托。(即Task.Run(异步()=>entities=Wait-DoAnotherWork(实体)).Wait()
)代码已经运行了一段时间,但现在我的团队成员删除了
Task.Run()
,并将其更改为DoAnotherWork(entities).Result代码>。我不确定这是否会导致死锁。
我没有问他为什么要修改它,但这一修改让我思考“我的代码好吗?为什么?”
我的问题是:
*两者之间有什么区别?
*哪种代码合适和/或安全(=不会导致死锁)?
*如果是,在什么情况下会导致死锁?
*为什么Task.Run()
解决我遇到的死锁?(详见下文)
注意:我使用的是.NETCore3.1
为什么使用Task.Run()
当我们使用AbcAsync().Result
或或Wait()
时,我的团队有几次出现死锁问题(该方法是在NET Core Web API方法中调用的,死锁主要发生在我们运行执行该方法的单元测试时),因此我们使用了Task.Run(异步()=>Wait AbcAsync()).Wait()/结果
之后,我们再也没有看到任何死锁问题。
但是,本页:表示在某些情况下,delagation将导致死机
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)...
}
公共类事件处理器:IEventProcessor
{
公共异步任务进程EventsAsync(PartitionContext上下文,IEnumerable消息)
{
...
var result=await eventHandler.ProcessAsync(消息);
...
}
}
公共任务异步进程异步(IEnumerable消息)
{
...
var实体=转换实体(消息);
...
}
公共列表转换实体(IEnumerable消息)
{
var serializedMessages=序列化(消息);
var entities=autoMapper.Map(serializedMessages);
//Task.Run(async()=>entities=Wait DoAnotherWork(entities)).Wait();//更改前
entities=DoAnotherWork(entities).Result;//更改后
返回实体;
}
公共任务异步实体[]DoNotherWork(实体[]实体)
{
//异步做事
等待DomorSteff(实体)。。。
}
这两者的区别是什么
Task.Run
开始在线程池线程上运行委托;直接调用该方法
在学习async
时,将所有内容分开是很有帮助的,这样您就可以准确地看到发生了什么:
entities = DoAnotherWork(entities).Result;
相当于:
var entitiesTask = DoAnotherWork(entities);
entities = entitiesTask.Result;
async Task LambdaAsMethod()
{
entities = await DoAnotherWork(entities);
}
var runTask = Task.Run(LambdaAsMethod);
runTask.Wait();
该代码:
Task.Run(async () => entities = await DoAnotherWork(entities)).Wait();
相当于:
var entitiesTask = DoAnotherWork(entities);
entities = entitiesTask.Result;
async Task LambdaAsMethod()
{
entities = await DoAnotherWork(entities);
}
var runTask = Task.Run(LambdaAsMethod);
runTask.Wait();
哪种代码合适和/或安全(=不会导致死锁)
在ASP.NET环境中,因为它将干扰ASP.NET对线程池的处理,并在不需要线程切换时强制执行线程切换
如果是,在什么情况下会导致僵局
这需要两件事:
阻止异步代码而不是正确使用wait
的代码
强制同步的上下文(即,一次只允许上下文中的一个代码块)
最好的解决办法是消除第一个条件;换句话说,使用。要在此处应用,最好的解决方案是完全消除阻塞:
public Task async ProcessAsync(IEnumerable<EventData> messages)
{
...
var entities = await ConvertToEntityAsync(messages);
...
}
public async Task<List<Entity>> ConvertToEntityAsync(IEnumerable<EventData> messages)
{
var serializedMessages = Serialize(messages);
var entities = autoMapper.Map<Entity[]>(serializedMessages);
entities = await DoAnotherWork(entities);
return entities;
}
public Task async ProcessAsync(IEnumerable消息)
{
...
var实体=等待ConvertToEntityAsync(消息);
...
}
公共异步任务ConvertToEntityAsync(IEnumerable消息)
{
var serializedMessages=序列化(消息);
var entities=autoMapper.Map(serializedMessages);
实体=等待完成其他工作(实体);
返回实体;
}
为什么Task.Run()会解决我遇到的死锁?(详见下文)
,因此它使用线程池上下文。由于.NETCore没有上下文,它删除了死锁的第二个条件,死锁将不会发生如果您正在ASP.NET核心项目中运行此功能
当我们使用AbcAsync().Result或.Wait()时,我的团队有几次出现死锁问题(该方法是在NET Core Web API方法中调用的,死锁主要发生在我们运行执行该方法的单元测试时)
一些单元测试框架确实提供了一个上下文——最著名的是xUnit。xUnit提供的上下文是一个同步上下文,因此它更像是UI上下文或ASP.NET预核心上下文。因此,当您的代码在单元测试中运行时,它确实存在死锁的第二个条件,并且死锁可能会发生
如上所述,最好的解决方案是完全消除堵塞;这将有一个很好的副作用,使您的服务器更高效。但是如果必须执行阻塞,那么您应该将单元测试代码包装在一个任务中。运行,而不是ASP.NET核心代码。另外,当两个线程最终等待相同的资源或彼此时,通常会发生死锁,两条线中的一条永远不会结束或向前移动。乔·阿尔巴哈里出色而自由的作品非常详细地解释了这一切。