简体   繁体   English

如果我以昂贵的方法返回Task,是否应该添加async修饰符?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM