C# 如何连接同步和异步?
我知道async/await的问题由来已久,但是,如果有人能帮我解决以下问题,那就太好了: 先决条件: .NetFramework。遗产C# 如何连接同步和异步?,c#,asp.net,async-await,task-parallel-library,C#,Asp.net,Async Await,Task Parallel Library,我知道async/await的问题由来已久,但是,如果有人能帮我解决以下问题,那就太好了: 先决条件: .NetFramework。遗产 具有同步动作方法的API控制器。 action方法调用一些私有方法,这些方法创建ISomething的对象 使用另一个类的public void Run(ISomething input)方法(该类的实例在控制器的构造函数中创建) 通过类似于库的异步库将ISomething对象发送到应用程序的另一部分。发送(ISomething输入) 由于publi
- 具有同步动作方法的API控制器。
- action方法调用一些私有方法,这些方法创建
的对象ISomething
- 使用另一个类的
方法(该类的实例在控制器的构造函数中创建)public void Run(ISomething input)
- 通过类似于
库的异步库将
对象发送到应用程序的另一部分。发送(ISomething输入)ISomething
- action方法调用一些私有方法,这些方法创建
public void Run(ISomething input)
是同步的,并且库的方法是异步的,因此在public void Run(ISomething input)
的引擎盖下有一个适配器。
整个画面如下所示:
// _lib.Send(ISomething input) returns Task here and performs IO operation
public void Run(ISomething input)
{
RunAsync(() => _lib.Send(ISomething input));
}
private void RunAsync(Func<Task> input)
{
var result = Task.Run(() =>
{
input.Invoke();
}).ConfigureAwait(false);
result.GetAwaiter().GetResult();
}
/\u lib.Send(ISomething输入)在此处返回任务并执行IO操作
公共作废运行(输入)
{
RunAsync(()=>_lib.Send(ISomething输入));
}
专用void RunAsync(Func输入)
{
var result=Task.Run(()=>
{
input.Invoke();
}).配置等待(错误);
result.GetAwaiter().GetResult();
}
我的问题是:
Task.Run()
中使用(如果可能,请解释)公共任务发送(输入)代码>
Run
依赖于异步\u lib.Send
,因此Run
应更改为异步。然后,调用Run
的控制器操作应更改为异步。然后,所有内容都是异步的,您不再具有通过异步反模式进行同步
然而,有时候这个答案并不现实,因为更新遗留代码需要开发人员的时间,而且通常服务器时间比开发人员的时间便宜。“go”的理想答案是为新代码提供很好的建议,但对于遗留代码可能并不现实。因此,在传统应用程序中存在着许多问题。这里使用的黑客是“线程池黑客”
是否有更好的方法包装_lib.Send(ISomething输入)
黑客的一个问题是,它们并不适用于所有场景。从同步代码调用异步代码没有完美的通用解决方案,而且也没有完美的通用解决方案。如果您必须通过异步反模式保持传统的同步,那么您只需要接受您需要使用的hack的限制
在这种情况下,线程池黑客有一个限制,即被包装的代码(\u lib.Send
)必须可以从裸线程池线程调用。由于这是一个旧版ASP.NET应用程序,这意味着代码不能依赖于ASP.NET上下文(包括HttpContext.Current
)。基于\u lib.Send
的名称和描述,我希望它不依赖于上下文,可以从任意线程调用,因此我希望线程池黑客应该适用于该代码
同样,这是基于这样一种理解,即最好使用任务RunAsync(ISomething输入)=>_lib.Send(输入)代码>,这样可以避免任务。完全运行
包装器。您甚至可以保留旧的void运行
实现,并根据需要逐渐将其余代码转换为使用RunAsync
。这就是我的建议
是否应在Task.Run()中使用async/await(如果可能,请解释)
是的。关键字应该在任何非平凡的代码中使用,但由于委托只是调用一个方法,因此在我看来没有必要这样做。调用同步方法的线程仍然必须等待整个操作完成。使用另一个线程会使情况变得更糟。异步的要点是,当长时间运行的非CPU操作完成时,应用程序使用的不是两个线程,甚至不是一个线程,而是零线程Task.GetAwaiter().GetResult()
是创建死锁的一种非常好的方法。例如,如果这是在UI线程上运行的,并且\u lib.Send
方法更新了IProgress
,则您将破坏您的应用程序。