C# 如何连接同步和异步?

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

我知道async/await的问题由来已久,但是,如果有人能帮我解决以下问题,那就太好了:

先决条件: .NetFramework。遗产

  • 具有同步动作方法的API控制器。
    • action方法调用一些私有方法,这些方法创建
      ISomething
      的对象
    • 使用另一个类的
      public void Run(ISomething input)
      方法(该类的实例在控制器的构造函数中创建)
    • 通过类似于
      库的异步库将
      ISomething
      对象发送到应用程序的另一部分。发送(ISomething输入)
由于
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();
}
我的问题是:

  • async/await是否应在
    Task.Run()
    中使用(如果可能,请解释)
  • 在上述条件下,是否有更好的方法包装库发送(ISomething输入)签名为:
    公共任务发送(输入)
  • 如何连接同步和异步

    最好的答案是“你没有”。在这种情况下,由于
    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
    ,则您将破坏您的应用程序。