[英]Should I add the async modifier if I return a Task in an expensive method?
The C# compiler already warns us if we have a method that has the async
modifier but does not use the await
operator. 如果我们有一个具有
async
修饰符但不使用await
运算符的方法,则C#编译器已经警告我们。
According to this answer there's no point adding an await
to the end of an async method (and in that case, just remove the async
modifier). 根据这个答案,没有必要在异步方法的末尾添加一个
await
(在这种情况下,只需删除async
修饰符即可)。
But what if the method has an expensive synchronous operation it needs to perform before calling a subsequent true async method? 但是,如果该方法需要执行昂贵的同步操作,然后再调用后续的真正异步方法,该怎么办?
For example, if I'm using HttpClient
: 例如,如果我使用
HttpClient
:
private readonly HttpClient client = ...
public Task<HttpResponseMessage> CallMyWebServiceMethod() {
HttpRequestMessage request = this.SomeExpensiveButSynchronousMethod();
return this.client.SendAsync( request );
}
This code would block the caller (due to SomeExpensiveButSynchronousMethod
). 此代码将阻止调用方(由于
SomeExpensiveButSynchronousMethod
)。
However, if I change the code to this: 但是,如果我将代码更改为此:
public async Task<HttpResponseMessage> CallMyWebServiceMethod() {
HttpRequestMessage request = this.SomeExpensiveButSynchronousMethod();
return await this.client.SendAsync( request );
}
and I call it like so: 我这样称呼它:
HttpResponse response = await myWrapper.CallMyWebServiceMethod();
...I understand the TPL would spin up a background thread immediately and then run CallMyWebServiceMethod
in the background thread, resuming whatever the parent code wants, making the entire call non-blocking in the process, before resuming after the Task completes and returns the HttpResponse
. ...我知道TPL会立即启动后台线程,然后在后台线程中运行
CallMyWebServiceMethod
,恢复父代码想要的任何内容,使整个调用在该过程中成为非阻塞状态,然后在Task完成并返回HttpResponse
。
...if that's the case then it seems contradictory. ...如果是这种情况,那似乎是矛盾的。
If I'm wrong, and the call is blocking until it gets to SendAsync
then how can I execute SomeExpensiveButSynchronousMethod
on the same background thread as the HttpClient
uses for its request? 如果我错了,并且调用一直阻塞,直到到达
SendAsync
那么如何在与HttpClient
用于其请求的同一后台线程上执行SomeExpensiveButSynchronousMethod
?
According to this answer there's no point adding an await to the end of an async method (and in that case, just remove the async modifier).
根据这个答案,没有必要在异步方法的末尾添加一个await(在这种情况下,只需删除async修饰符即可)。
That's an oversimplification. 这太简单了。 See my blog post on eliding async and await .
参见我关于消除异步和等待的博客文章。
But what if the method has an expensive synchronous operation it needs to perform before calling a subsequent true async method?
但是,如果该方法需要执行昂贵的同步操作,然后再调用后续的真正异步方法,该怎么办?
This is a rare case, but the appropriate solution IMO is to execute the synchronous operation synchronously (ie, not wrapped in a Task.Run
) and be sure to document its behavior . 这种情况很少见,但是IMO的适当解决方案是同步执行同步操作(即,不包装在
Task.Run
),并确保记录其行为 。
I understand the TPL would spin up a background thread immediately and then run CallMyWebServiceMethod in the background thread, resuming whatever the parent code wants, making the entire call non-blocking in the process, before resuming after the Task completes and returns the HttpResponse.
我知道TPL会立即启动后台线程,然后在后台线程中运行CallMyWebServiceMethod,恢复父代码想要的任何内容,使整个调用在该过程中成为非阻塞状态,然后在Task完成并返回HttpResponse之后继续执行。
That is not at all what happens. 那根本不会发生什么。 You may find my
async
intro helpful. 您可能会发现我的
async
介绍很有帮助。 Quote: 引用:
The beginning of an async method is executed just like any other method.
异步方法的开始与其他方法一样执行。 That is, it runs synchronously until it hits an “await” (or throws an exception).
也就是说,它会同步运行,直到达到“ await”(或引发异常)为止。
In actuality, both of your examples synchronously block the caller while executing SomeExpensiveButSynchronousMethod
. 实际上,您的两个示例在执行
SomeExpensiveButSynchronousMethod
都同步阻塞了调用者。
If I'm wrong, and the call is blocking until it gets to SendAsync then how can I execute SomeExpensiveButSynchronousMethod on the same background thread as the HttpClient uses for its request?
如果我错了,并且调用一直阻塞,直到到达SendAsync,那么如何在与HttpClient用于其请求的同一后台线程上执行SomeExpensiveButSynchronousMethod?
HttpClient
doesn't use a background thread for its request, so this part of the question doesn't make sense. HttpClient
的请求不使用后台线程,因此问题的这一部分没有意义。 For more information on how asynchronous I/O works, see my blog post There Is No Thread . 有关异步I / O如何工作的更多信息,请参见我的博客文章There No Thread 。
To answer the actual question: 要回答实际问题:
Should I add the async modifier if I return a Task in an expensive method?
如果我以昂贵的方法返回Task,是否应该添加async修饰符?
Yes. 是。 But the reason you should do so is not to "make it asynchronous";
但是,您这样做的原因不是“使其异步”; it's so that any exceptions from
SomeExpensiveButSynchronousMethod
are captured and placed on the returned Task
, which is the expected semantic for methods that follow the Task-based Asynchronous Pattern (TAP). 这样一来,将捕获
SomeExpensiveButSynchronousMethod
中的所有异常并将其放置在返回的Task
,这是遵循基于任务的异步模式(TAP)的方法的预期语义。
I understand the TPL would spin up a background thread immediately and then run
CallMyWebServiceMethod
我知道TPL会立即启动后台线程,然后运行
CallMyWebServiceMethod
No, that's not what happens. 不,那不会发生。 The first synchronous part of an
async
method executes synchronously. async
方法的第一个同步部分是async
执行的。 If you want make sure it does not block the current thread, you should use Task.Run()
. 如果要确保它不会阻塞当前线程,则应使用
Task.Run()
。
Your expensive method is going to block the caller either way. 您昂贵的方法将以两种方式阻止调用者。
async
doesn't magically create threads for you, that's what Task
does (sometimes). async
不会神奇地为您创建线程,这就是Task
所做的(有时)。 So, if you add async
and await the SendAsync
, you're just adding state-machine overhead needlessly. 因此,如果添加
async
并等待SendAsync
,则只会不必要地增加状态机的开销。
see also http://blog.stephencleary.com/2016/12/eliding-async-await.html 另请参阅http://blog.stephencleary.com/2016/12/eliding-async-await.html
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.