有好几次,我发现自己编写了长时间运行的异步方法,比如轮询循环.这些方法可能如下所示:
private async Task PollLoop() { while (this.KeepPolling) { var response = await someHttpClient.GetAsync(...).ConfigureAwait(false); var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); // do something with content await Task.Delay(timeBetweenPolls).ConfigureAwait(false); } }
为此目的使用异步的目的是我们不需要专用的轮询线程,但逻辑(对我而言)比直接使用像计时器这样的东西更容易理解(也就是说,不需要担心重入).
我的问题是,从同步上下文启动这样一个循环的首选方法是什么?我能想到至少两种方法:
var pollingTask = Task.Run(async () => await this.PollLoop()); // or var pollingTask = this.PollLoop();
在任何一种情况下,我都可以使用ContinueWith()来响应异常.我对这两种方法之间差异的主要理解是,第一个将首先在线程池线程上开始循环,而第二个将在当前线程上运行,直到第一个等待.这是真的?还有其他需要考虑的事项或更好的方法吗?
解决方法
My main understanding of the difference between these two methods is
that the first will initially start looping on a thread-pool thread,
whereas the second will run on the current thread until the first
await. Is this true?
是.异步方法在第一次等待尚未完成的等待时将其任务返回给其调用者.
按照惯例,大多数异步方法返回非常快.你也可以,因为等待一些HttpClient.GetAsync将很快到达.
将这个异步方法的开头移到线程池上是没有意义的.它增加了开销,几乎没有延迟.它当然无助于吞吐量或扩展行为.
在这里使用异步lambda(Task.Run(async()=> await this.PollLoop()))尤其无用.它只是将PollLoop返回的任务包含在另一层任务中.最好说Task.Run(()=> this.PollLoop()).