Task.Run和直接异步调用对于启动长时间运行的异步方法有什么区别?

7

我发现自己经常需要编写长时间运行的异步方法,例如轮询循环。这些方法可能会像这样:

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()来响应异常。我对这两种方法的主要理解是,第一种方法最初会在线程池线程上开始循环,而第二种方法将在当前线程上运行,直到第一个await。这是真的吗?还有其他需要考虑或尝试的更好方法吗?

1
在第一个中,我看到了3个任务.. 1) Task.Run 2) 由编译器生成的await 3) PollLoop本身。另一方面,第二个只有1个任务。 - I4V
如果您将第一个版本缩减为2个任务版本,您可以更好地看到答案:Task.Run(() => this.PollLoop().Wait()) - I4V
如果这是服务器端代码,你无论如何都不需要使用 Task.Run - noseratio - open to work
但是,如果此任务有很多代码并且从Windows服务启动,则服务启动将等待await...,这可能需要服务长时间运行。因此,一个技巧就是在开头放置await Task.Delay(10); - Rui Caramalho
2个回答

7
我对这两种方法的主要理解是,第一种方法最初会在线程池线程上开始循环,而第二种方法会在当前线程上运行,直到第一个await。这是真的吗?
是的。异步方法在等待一个尚未完成的可等待对象的第一个await时将其任务返回给调用者。
按照惯例,大多数异步方法都非常快速地返回。您的方法也是如此,因为 await someHttpClient.GetAsync 将非常快地被执行。
将此异步方法的开头移动到线程池中没有意义。它增加了开销,但几乎不会节省延迟。它肯定不会帮助吞吐量或扩展行为。
在这里使用异步 lambda(Task.Run(async () => await this.PollLoop()))特别无用。它只是用另一层任务包装了 PollLoop 返回的任务。更好的做法是 Task.Run(() => this.PollLoop())。

实际上,Task.Run(() => this.PollLoop()) 也是无用的。只需要 this.PollLoop() 就足够了... - EZI
@EZI 有点类似。他特别询问了这个问题,我在我的回答中讨论了这个问题。 - usr
你们讨论了这个问题,但是说Task.Run(() => this.PollLoop())会更好,因为它会在PollLoop周围创建一个多余的任务。如果不使用await调用PollLoop();,它已经会在一个任务中运行了。 - EZI
@EZI 嗯,一级冗余比两级冗余好! :) - usr

3
我的主要理解是,第一种方法最初会在线程池线程上开始循环,而第二种方法将在当前线程上运行,直到第一个await。这是真的吗?
是的,没错。
在您的情况下,似乎没有必要使用Task.Run,因为在方法调用和第一个await之间实际上几乎没有代码,因此PollLoop()几乎会立即返回。不必要地将任务包装在另一个任务中只会使代码难以阅读并增加开销。我宁愿使用第二种方法。
关于其他考虑(例如异常处理),我认为这两种方法是等价的。
使用异步的目的是我们不需要专用的轮询线程,但逻辑(对我来说)比直接使用定时器更容易理解
顺便说一下,这基本上就是定时器所做的事情。实际上,Task.Delay是使用定时器实现的!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接