【问题标题】:Converting a series of synchronous methods into async in C#在C#中将一系列同步方法转换为async
【发布时间】:2014-07-29 06:25:21
【问题描述】:

如何将这个同步方法调用链转换为异步(使用 async/await 运算符)?鉴于只有最后一个调用 DoRequest() 是需要时间执行的调用,那是唯一需要变为异步的方法吗?或者链中的所有调用者,RequestSomething() 和 Process(),是否也需要异步?

[HttpGet]
void Process()
{
   var url = "http://someapi.com";
   var myObject= RequestSomething(url);
   //do something with the myObject.
}    

MyObject RequestSomething(string url)
{
   var request = new HttpRequestMessage(HttpMethod.Get, url);
   var response = DoRequest(request);
   return JsonConvert.DeserializeObject<MyObject>(response);
}

//method that takes time to return.
HttpResponseMessage DoRequest(HttpRequestMessage request)
{
    var client = new HttpClient();
    return client.SendAsync(request).Result;
}

【问题讨论】:

  • RequestSomething 什么都不做。仅调用一个方法来返回其结果不会增加任何内容。您可以完全删除它。而Process 所做的只是调用一个方法并忽略返回值。你真的需要这两种方法吗?
  • 这些位于我项目的不同层...我只是将它们放在一起以保持简单。
  • @Servy 我已经更新了我的问题以澄清我在问什么。
  • 确保你遵守约定,你的方法被称为ProcessAsyncRequestSomethingAsyncDoRequestAsync 尊重。

标签: c# .net asynchronous async-await


【解决方案1】:

要正确执行异步,它是“具有传染性的”,如果您在一个地方执行此操作,则需要在调用链的整个过程中执行此操作才能从中获得任何真正的好处。因此,无论调用 Process() 什么都需要处理从 Process 返回的任务,方法是等待它或像 DoRequest 那样将其向上传递。

async Task Process()
{
   var url = "http://someapi.com";
   var myObject= await RequestSomething(url);
   //do something with the myObject.
}    

async Task<MyObject> RequestSomething(string url)
{
   var request = new HttpRequestMessage(HttpMethod.Get, url);
   var response = await DoRequest(request).ConfigureAwait(false);
   return JsonConvert.DeserializeObject<MyObject>(response);
}

//method that takes time to return.
Task<HttpResponseMessage> DoRequest(HttpRequestMessage request)
{
    var client = new HttpClient();
    return client.SendAsync(request);
}

因为在你的DoRequest 函数中你不需要 async/await 的请求后你没有做任何额外的工作,但是其他的需要 async/await 关键字。 .ConfigureAwait(false) 使得函数不必在 UI 线程上运行其其余代码,这可以给您带来小的性能提升。我不知道//do something with the myObject. 中的代码是否要求你在 UI 线程上,所以我没有把它放在那个 await 上,但如果你不需要在 UI 线程上,你可以添加它那里也有。

【讨论】:

  • 谢谢斯科特。这开始有意义了。几个问题。所以await关键字一般只会在await之后的方法中有其他事情要做的时候才使用,对吧?您永远不会真正等待该方法的最后一条语句吗?另一个问题是,如果 Process() 是一种通过外部源获取调用的 Web API 方法,那么我是否可以将其设为无效,并改为执行 RequestSomething(url).Start() ?
  • .Start()是不行的,事实上当函数返回时任务已经开始了,await更像.Result或者.Wait()。请注意,在与await 相同的代码中使用.Result.Wait() 可能会导致系统死锁。这就是为什么我说如果你要做一件异步的事情,你需要让你的整个调用链异步。您不应该在中途打电话给.Wait()。你用的是哪个WebAPI,MVC支持返回Task而不是voidTask&lt;T&gt;而不是T
  • 我使用的是常规的 .NET Web API。
  • @Prabhu 即使你是最后一行,你可能想要等待的一件事是,如果抛出异常,它会发生在等待点而不是隐藏在 Task 内,如果你认为您的调用者也不会等待您的任务,但需要通知您可能发生了异常,然后在您返回之前等待。
  • 我不明白您是如何使用 Process() 的,那么,如果您仍有疑问,可以用一个更大的示例来更新您的问题,展示如何调用 Process()
【解决方案2】:

你应该做 DoRequest...

Public async Task<HttpResponseMessage> DoRequest(...

然后返回 await client.SendAsync

同样,DoSomething 应该是异步任务<...>

您的调用方法 Process 如果您想将其称为 fire and forget,则可以是 async void,否则为 async Task。

【讨论】:

  • 谢谢。那么它们都应该变成异步的吗?如果我只是将第一个方法 Process() 设为异步,并等待 RequestSomething 的结果,那还不够吗?
  • 使它们全部异步是其中的一部分。如果您希望 Process 处于可等待状态,则它必须返回一个 Task,否则返回 void 并且可以调用它来触发异步,但不必在主线程继续执行之前返回。
  • 能否 RequestSomething 和 DoRequest 都返回 Task(而不是异步),而只是 Process 是异步的,这一切都是异步的?
  • 对于“否则返回无效”的评论,我几乎想给你一个 -1 的答案。除非您正在等待事件处理程序,否则您永远不应该返回 void。 @Prabhu 回答你的问题,不,你不能因为你需要 DoRequest 的完成结果将它传递给 DeserializeObject
  • Async void 是一个有效的构造@scottchamberlain。我在这里介绍了它,并解释了它对谁决定的作用。您是正确的,它通常作为事件处理程序处理,但它也可以用于在类构造函数期间促进异步操作。你警告它是对的,但说 NEVER 有点戏剧化。
【解决方案3】:

试试:

var task = Task.Run(() => RequestSomething());
var task2 = Task.Run(() => RequestSomethingElse());

await Task.WhenAll(task, task2);

您可以指定两个以上,也可以只指定一个。

【讨论】:

  • 这绝对是错误的,不是要走的路。不需要使用线程池线程来执行异步 IO 绑定工作。
猜你喜欢
  • 2018-05-10
  • 2013-03-09
  • 2012-10-25
  • 2021-04-12
  • 1970-01-01
  • 1970-01-01
  • 2019-12-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多