使用Task.WhenAll与BlockingCollection生成的无限任务

时间:2014-08-25 14:48:22

标签: c# task async-await blockingcollection

我将后台任务添加到阻止集合(在后台添加)。

我在GetConsumingEnumerable返回的Enumerable上等待Task.WhenAll。

我的问题是:Task.WhenAll的重载是否接收到IEnumerable"准备好了#34;可能会收到无穷无尽的任务?

我只是不确定我是否可以这样做,或者是否以这种方式使用?

private async Task RunAsync(TimeSpan delay, CancellationToken cancellationToken)
{
    using (BlockingCollection<Task> jobcollection = new BlockingCollection<Task>())
    {
        Task addingTask = Task.Run(async () =>
        {
            while (true)
            {
                DateTime utcNow = DateTime.UtcNow;
                var jobs = Repository.GetAllJobs();
                foreach (var job in GetRootJobsDue(jobs, utcNow))
                {
                    jobcollection.Add(Task.Run(() => RunJob(job, jobs, cancellationToken, utcNow), cancellationToken), cancellationToken);
                }

                await Task.Delay(delay, cancellationToken);
            }
        }, cancellationToken);

        await Task.WhenAll(jobcollection.GetConsumingEnumerable(cancellationToken));
    }
}

3 个答案:

答案 0 :(得分:6)

Since your goal is merely to wait until the cancellation token is cancelled,你应该那个。出于其他人解释的原因,在无限的任务序列上使用WhenAll并不是解决这个问题的方法。有更简单的方法来完成永远不会完成的任务。

await new TaskCompletionSource<bool>().Task
    .ContinueWith(t => { }, cancellationToken);

答案 1 :(得分:5)

Task.WhenAll不适用于无数个任务。它将首先(同步)等待枚举完成,然后(异步)等待它们全部完成。

如果要以异步方式对序列作出反应,则需要使用IObservable<Task>(Reactive Extensions)。您可以使用TPL数据流BufferBlock作为&#34;队列&#34;可以使用同步或异步代码,并且可以轻松转换为IObservable<Task>

答案 2 :(得分:0)

我假设Task.WhenAll将尝试枚举集合,这意味着它本身将阻塞,直到集合完成或取消。如果没有,那么代码理论上可以在创建任务之前完成await。所以在那里会有一个额外的块......它将阻止等待创建线程,然后再次阻塞,直到任务完成。我不认为这对你的代码来说是件坏事,因为它仍然会在同一时间段内阻止。