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

C# 用于启动长时间运行异步方法的Task.Run与直接异步调用,c#,task-parallel-library,async-await,C#,Task Parallel Library,Async Await,有好几次,我发现自己为轮询循环之类的东西编写了长时间运行的异步方法。这些方法可能如下所示: private async Task PollLoop() { while (this.KeepPolling) { var response = await someHttpClient.GetAsync(...).ConfigureAwait(false); var content = await response.Content.ReadAsStrin

有好几次,我发现自己为轮询循环之类的东西编写了长时间运行的异步方法。这些方法可能如下所示:

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);
    }
}
为此目的使用async的目的是,我们不需要专用的轮询线程,但逻辑(对我来说)比直接使用计时器之类的东西更容易理解(也不需要担心重入)

我的问题是,从同步上下文启动这样一个循环的首选方法是什么?我至少可以想到两种方法:

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

// or

var pollingTask = this.PollLoop();
无论哪种情况,我都可以使用ContinueWith()响应异常。我对这两种方法之间的区别的主要理解是,第一种方法最初将在线程池线程上开始循环,而第二种方法将在当前线程上运行,直到第一种方法等待。这是真的吗?还有其他需要考虑的事情还是更好的尝试?

我对这两种方法之间差异的主要理解是 第一个线程最初将在线程池线程上开始循环, 而第二个线程将在当前线程上运行,直到第一个线程 等待。这是真的吗

对。异步方法在尚未完成的等待对象的第一次等待时将其任务返回给调用方

按照惯例,大多数异步方法返回速度都非常快。您的也可以,因为
正在等待某个HttpClient。GetAsync
将很快到达

将此异步方法的开头移动到线程池中没有意义。它增加了开销,几乎没有延迟。这当然无助于吞吐量或扩展行为

在这里使用异步lambda(
Task.Run(async()=>wait this.PollLoop())
)尤其无用。它只是用另一层任务包装由
PollLoop
返回的任务。最好说
Task.Run(()=>this.PollLoop())

我对这两种方法之间的区别的主要理解是,第一种方法最初将在线程池线程上开始循环,而第二种方法将在当前线程上运行,直到第一种方法等待。这是真的吗

是的,没错

在您的场景中,似乎不需要使用
任务。尽管如此,在方法调用和第一个
wait
之间实际上没有代码,因此
PollLoop()
几乎会立即返回。不必要地将一个任务包装到另一个任务中只会降低代码的可读性并增加开销。我宁愿使用第二种方法

关于其他考虑(例如异常处理),我认为这两种方法是等效的

为此目的使用async的目的是,我们不需要专用的轮询线程,但是(对我来说)逻辑比直接使用计时器之类的东西更容易理解


作为旁注,这或多或少是一个计时器会做的事情。事实上,
Task.Delay
正在使用计时器

实际上,
Task.Run(()=>this.PollLoop())
也没用
this.PollLoop()
足够了…@EZI有点像。他特别问了这个问题,我在回答中对此进行了讨论。你讨论了这个问题,但说是
Task.Run(()=>this.PollLoop())
会更好,这样会创建一个冗余任务。调用
PollLoop()不等待将已经在任务中运行。@EZI嗯,一级冗余比两级冗余好!:)在第一个例子中,我看到了3个任务。。1) 
Task.Run
2)因为
wait
(由编译器生成)3)轮询循环本身。另一方面,第二个只有1个任务。如果将第一个任务版本减少为2个任务版本,则可以更好地看到答案:
Task.Run(()=>this.PollLoop().Wait())
如果这是服务器端代码,则不需要
Task.Run