【问题标题】:Any reason to use async/await when the controller action is sync?当控制器动作同步时,有什么理由使用 async/await 吗?
【发布时间】:2015-11-25 18:40:33
【问题描述】:

假设我有一个控制器操作不能异步(由于各种原因),但我有一个服务(通过多种方法)使用HttpClient 调用休息服务。使用异步客户端和使用.Wait 或.Result 有什么好处吗?还是使用同步方式会不会性能更差?

所以要么:

//MyController.cs
public ActionResult GetSomething(int id)
{
    //lots of stuff here
    var processedResponse = _myService.Get(id);
    //lots more stuff here
    return new ContentResult(result);
}

//MyService.cs
public ProcessedResponse Get(int id)
{
    var client = new HttpClient();
    var result = client.Get(_url+id);
    return Process(result);
}

或者:

//MyController.cs
public ActionResult GetSomething(int id)
{
    //lots of stuff here
    var processedResponse = _myService.GetAsync(id).Result;
    //or, .Wait, or Task.Run(...), or something similar
    //lots more stuff here
    return new ContentResult(result);
}

//MyService.cs
public async Task<ProcessedResponse> GetAsync(int id)
{
    var client = new HttpClient();
    var result = await client.GetAsync(_url+id);
    return await Process(result);
}

【问题讨论】:

  • IMO,创建服务方法async 的唯一原因是计划将控制器操作重写为async。
  • @Marius:很想知道阻止异步控制器的“各种原因”是什么。

标签: c# asp.net asynchronous async-await


【解决方案1】:

使用异步客户端并包装 Task.Run(() => _myService.Get()).Result 中的方法?

您最有可能最终获得的唯一东西是僵局。想想看,你在线程池线程上排队一个自然的异步方法,ASP.NET 已经给你一个线程来处理你里面的 Action。没有多大意义。

如果您想要异步,并且认为您实际上会从异步提供的规模中受益,那么您应该将您的控制器也重新考虑为异步并返回 Task&lt;T&gt; ,您可以在其中await 那些异步方法。

所以我要么保持同步,要么从上到下重构代码以支持异步:

//MyController.cs
public async Task<ActionResult> GetSomethingAsync(int id)
{
    //lots of stuff here
    await GetAsync(id);
    return new ContentResult(result);
}

//MyService.cs
public async Task<ProcessedResponse> GetAsync(int id)
{
    var client = new HttpClient();
    var result = await client.GetAsync(_url+id);
    return await Process(result);
}

【讨论】:

  • 您能否添加一个示例,说明通过混合同步和异步代码会如何发生死锁..?
  • 请注意,我并不是专门询问Task.Run(),而是询问将任务转换为同步结果的任何方法(如.Wait 和.Result)。我已经更新了问题,所以如果这些方式之间有任何区别,请更新您的答案以反映这一点
  • @eranotzap Here are some examples (注意每个单词都是不同的链接:))
【解决方案2】:

在您的场景中,没有充分的理由,但让我们添加一些功能:

//MyController.cs
public ActionResult GetSomething(int id)
{
    //lots of stuff here
    var processedResponse = _myService.GetAsync(id).Result;
    //or, .Wait, or Task.Run(...), or something similar
    //lots more stuff here
    return new ContentResult(result);
}

//MyService.cs
public async Task<ProcessedResponse> GetAsync(int id)
{
    var client = new HttpClient();
    var pendingResult1 = client.GetAsync(_url+id);
    var pendingResult2 = someAsyncOperation();
    var result3 = someSyncOperation();
    var result1 = await pendingResult;
    var result2 = await pendingResult2;
    return await Process(result1, result2, result3);
}

现在,由于您的请求需要一段时间才能完成,someAsynchOperation 立即开始执行,而不是等待 GetAsync() 完成。同时someSyncOperation也在执行中。

如果没有async 关键字,您将无法使用await,因此如果您计划在您的函数中进行异步执行,最好使用它。

【讨论】:

  • 注意:如果您选择等待,请使用 task.GetAwaiter().GetResult();而不是 task.Result 所以它不会在 AggregateException 中包装异常。
【解决方案3】:

do 能做到这一点会很有趣

//MyController.cs
public ActionResult GetSomething(int id)
{
    var processedResponseTask = _myService.GetAsyn(id)
    //lots of stuff here (1)

    var processedResponseTask.Wait();
    var processedResponse = processedResponseTask.Result;

    //lots more stuff here (2)
    return new ContentResult(result);
}

现在这里(1) 的许多内容与您的异步任务并行完成。 (或者,例如,如果您两次调用您的服务)。如果您实际上并没有在此处 (1) 做很多事情,那么就没有什么意义了。

【讨论】:

    猜你喜欢
    • 2021-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-02
    • 2017-10-26
    • 1970-01-01
    • 2018-02-06
    相关资源
    最近更新 更多