如何停止等待任务,但让任务在后台运行?

时间:2019-02-26 20:47:00

标签: c# multithreading asynchronous async-await

如果我有要一起执行的任务列表,但同时又想一起执行一定数量的任务,那么我等待其中一个任务直到其中一个任务完成,这意味着等待应该停止并应该开始一个新任务,但是当其中一个完成时,我不知道如何停止等待当前正在等待的任务,我不想取消该任务,只是停止等待并让它继续在后台运行。

我有以下代码

foreach (var link in SharedVars.DownloadQueue)
{
    if (currentRunningCount != batch)
    {
        var task = DownloadFile(extraPathPerLink, link, totalLen);
        _ = task.ContinueWith(_ =>
        {
            downloadQueueLinksTasks.Remove(task);
            currentRunningCount--;
            // TODO SHOULD CHANGE WHAT IS AWAITED
        });
        currentRunningCount++;
        downloadQueueLinksTasks.Add(task);
    }

    if (currentRunningCount == batch)
    {
        // TODO SHOULD NOT AWAIT 0
        await downloadQueueLinksTasks[0];
    }
}

我发现了大约Task.WhenAny,但是从this comment那里我了解到其他任务将被忽略,所以这不是我想要实现的解决方案。

对于这个问题是愚蠢还是错误,我感到很抱歉,但是我似乎找不到任何有关如何解决它的信息,或者我想要实现的操作的名称是什么,因此我什至可以正确地进行搜索。

解决方案修改

提供的所有答案都是正确的,我接受了我决定使用的答案,但仍然都是正确的。

谢谢大家,我从这些不同的答案和解决问题的方式以及思考的方式中学到了很多东西。

关于这个特定问题的了解是,我仍然需要等待剩下的其他任务,因此解决方案是将Task.WhenAny放入循环中(返回完成的任务(这也很重要)) ) AND Task.WhenAll,以等待其他剩下的任务。

4 个答案:

答案 0 :(得分:2)

Task.WhenAny返回已完成的任务。

/usr/bin/ping -c1 -W400 tools-dev1.example.com

答案 1 :(得分:2)

您要问的是节制,对于异步代码,最好通过SemaphoreSlim来表示:

var semaphore = new SemaphoreSlim(batch);
var tasks = SharedVars.DownloadQueue.Select(link =>
{
  await semaphore.WaitAsync();
  try { return DownloadFile(extraPathPerLink, link, totalLen); }
  finally { semaphore.Release(); }
});
var results = await Task.WhenAll(tasks);

答案 2 :(得分:1)

您可以使用Task.WaitAny()

这是该行为的演示:

public static async Task Main(string[] args)
{
     IList<Task> tasks = new List<Task>();

    tasks.Add(TestAsync(0));
    tasks.Add(TestAsync(1));
    tasks.Add(TestAsync(2));
    tasks.Add(TestAsync(3));
    tasks.Add(TestAsync(4));
    tasks.Add(TestAsync(5));

    var result = Task.WaitAny(tasks.ToArray());

    Console.WriteLine("returned task id is {0}", result);

    ///do other operations where

    //before exiting wait for other tasks so that your tasks won't get cancellation signal
    await Task.WhenAll(tasks.ToArray());

}

public static async Task TestAsync(int i)
{
    Console.WriteLine("Staring to wait" + i);
    await Task.Delay(new Random().Next(1000, 10000));
    Console.WriteLine("Task finished" + i);
}

输出:

Staring to wait0
Staring to wait1
Staring to wait2
Staring to wait3
Staring to wait4
Staring to wait5
Task finished0
returned task id is 0
Task finished4
Task finished2
Task finished1
Task finished5
Task finished3

答案 3 :(得分:1)

您应该使用Microsoft的Reactive Framework(又名Rx)-NuGet System.Reactive并添加using System.Reactive.Linq;-然后您可以执行以下操作:

IDisposable subscription =
    SharedVars.DownloadQueue
        .ToObservable()
        .Select(link =>
            Observable.FromAsync(() => DownloadFile(extraPathPerLink, link, totalLen)))
        .Merge(batch) //max concurrent downloads
        .Subscribe(file =>
        {
            /* process downloaded file here
               (no longer a task) */
        });

如果您需要先停止下载,然后再自然结束,只需致电subscription.Dispose()