Как мне прекратить ожидание задачи, но оставить работу в фоновом режиме? - PullRequest
0 голосов
/ 26 февраля 2019

Если у меня есть список задач, которые я хочу выполнить вместе, но в то же время я хочу выполнить определенное количество из них вместе, поэтому я жду одну из них, пока одна из них не завершится, что тогда должно означать ожиданиедолжен остановиться, и новая задача должна быть разрешена для запуска, но когда одна из них завершается, я не знаю, как перестать ожидать задачу, ожидаемую в данный момент, я не хочу отменить задачу, просто перестану ждатьи пусть он продолжает работать в фоновом режиме.

У меня есть следующий код

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, но из этого комментария здесь я понял, чтодругие задачи будут игнорироваться, поэтому это не то решение, которого я хочу достичь.

Извините, если вопрос глупый или неправильный, но я не могу найти какую-либо информацию о том, как его решить, иликак называется операция, которую я хочу выполнить, чтобы я мог даже правильно искать.

Решение Редактировать

Все предоставленные ответы верны , я принял тот, который решил использовать, но все они верны.

Спасибо всем, я многому научился у всех вас из этих разных ответов и разных способов решения проблемы и какподумать об этом.

Что я узнал об этой конкретной проблеме, так это то, что мне все еще нужно было дождаться других оставшихся задач, поэтому решение состояло в том, чтобы Task.WhenAny был внутри цикла (который возвращает законченную задачу(это также важно)) И Task.WhenAll вне цикла для ожидания других левых задач.

Ответы [ 4 ]

0 голосов
/ 27 февраля 2019

То, о чем вы спрашиваете, это throttling , что для асинхронного кода лучше всего выражается через 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);
0 голосов
/ 27 февраля 2019

Вы должны использовать 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().

0 голосов
/ 27 февраля 2019

Task.WhenAny возвращает выполненное задание.

foreach (var link in SharedVars.DownloadQueue)
{
    var task = DownloadFile(extraPathPerLink, link, totalLen);
    downloadQueueLinksTasks.Add(task);

    if (downloadQueueLinksTasks.Count == batch)
    {
        // Wait for any Task to complete, then remove it from
        // the list of pending tasks.
        var completedTask = await Task.WhenAny(downloadQueueLinksTasks);
        downloadQueueLinksTasks.Remove(completedTask);
    }
}

// Wait for all of the remaining Tasks to complete
await Task.WhenAll(downloadQueueLinksTasks);
0 голосов
/ 27 февраля 2019

Вы можете использовать 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
...