简体   繁体   English

Task.Run与直接异步调用,用于启动长时间运行的异步方法

[英]Task.Run vs. direct async call for starting long-running async methods

Several times, I have found myself writing long-running async methods for things like polling loops. 有好几次,我发现自己编写了长时间运行的异步方法,比如轮询循环。 These methods might look something like this: 这些方法可能如下所示:

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);
    }
}

The goal of using async for this purpose is that we don't need a dedicated polling thread and yet the logic is (to me) easier to understand than using something like a timer directly (also, no need to worry about reentrance). 为此目的使用async的目的是我们不需要专用的轮询线程,但是(对我而言)逻辑比直接使用像计时器这样的东西更容易理解(也就是说,不需要担心重入)。

My question is, what is the preferred method for launching such a loop from a synchronous context? 我的问题是,从同步上下文启动这样一个循环的首选方法是什么? I can think of at least 2 approaches: 我能想到至少两种方法:

var pollingTask = Task.Run(async () => await this.PollLoop());

// or

var pollingTask = this.PollLoop();

In either case, I can respond to exceptions using ContinueWith(). 无论哪种情况,我都可以使用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? 这是真的? Are there other things to consider or better approaches to try? 还有其他需要考虑的事项或更好的尝试方法吗?

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? 这是真的?

Yes. 是。 An async method returns its task to its caller on the first await of an awaitable that is not already completed. 异步方法在第一次等待尚未完成的等待时将其任务返回给其调用者。

By convention most async methods return very quickly. 按照惯例,大多数异步方法返回非常快。 Yours does as well because await someHttpClient.GetAsync will be reached very quickly. 您也可以,因为await someHttpClient.GetAsync将很快到达。

There is no point in moving the beginning of this async method onto the thread-pool. 将这个异步方法的开头移到线程池上是没有意义的。 It adds overhead and saves almost no latency. 它增加了开销,几乎没有延迟。 It certainly does not help throughput or scaling behavior. 它当然无助于吞吐量或扩展行为。

Using an async lambda here ( Task.Run(async () => await this.PollLoop()) ) is especially useless. 在这里使用异步lambda( Task.Run(async () => await this.PollLoop()) )特别无用。 It just wraps the task returned by PollLoop with another layer of tasks. 它只是将PollLoop返回的任务包含在另一层任务中。 it would be better to say Task.Run(() => this.PollLoop()) . Task.Run(() => this.PollLoop())会更好。

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? 这是真的?

Yes, that's true. 是的,这是真的。

In your scenario, there seem to be no need for using Task.Run though, there's practically no code between the method call and the first await , and so PollLoop() will return almost immediately. 在你的场景中,似乎没有必要使用Task.Run ,但是方法调用和第一个await之间几乎没有代码,所以PollLoop()几乎会立即返回。 Needlessly wrapping a task in another task only makes the code less readable and adds overhead. 不必要地将任务包装在另一个任务中只会降低代码的可读性并增加开销。 I would rather use the second approach. 我宁愿使用第二种方法。

Regarding other considerations (eg exception handling), I think the two approaches are equivalent. 关于其他考虑因素(例如异常处理),我认为这两种方法是等效的。

The goal of using async for this purpose is that we don't need a dedicated polling thread and yet the logic is (to me) easier to understand than using something like a timer directly 为此目的使用async的目的是我们不需要专用的轮询线程,但是(对我而言)逻辑比直接使用像timer这样的东西更容易理解

As a side-note, this is more or less what a timer would do anyway. 作为旁注,这或多或少都是计时器无论如何都会做的。 In fact Task.Delay is implemented using a timer! 实际上Task.Delay 使用计时器实现的!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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