Добавление анонимной задачи в список <Task>не выполняет ее после вызова .WaitAll () C # - PullRequest
0 голосов
/ 25 июня 2018

Я добавляю задачу в список задач следующим образом

taskList.Add(new Task(async () =>
    {
        // work here
        await MethodInsideThatNeedsAwaiting(); // if that has anything to do with it
        // more work 
    }));

Вызов Task.WaitAll(tasklist); впоследствии застревает.Программа продолжает работать, но больше ничего не слышно ни от каких задач из этого списка, и при этом она не достигает каких-либо точек останова, как будто она где-то застряла в своем собственном внутреннем асинхронном цикле.

Неправильно ли я добавляю задачу в список?В чем здесь проблема?

То, что я также пробовал:
Я также попробовал следующее, если по какой-то причине ключевое слово async было проблемой, но оно все еще не работает :

taskList.Add(new Task(() =>
    {
        // work here
        MethodInsideThatNeedsAwaiting().Wait();
        // more work 
    }));

Тем не менее работает как ожидалось

private async Task GetStuff()
{
    // same work here
    await MethodInsideThatNeedsAwaiting();
    // more work
}

А затем добавив его с помощью taskList.Add(GetStuff()); Вызов Task.WaitAll(tasklist); не имеет проблем с этим.

1 Ответ

0 голосов
/ 25 июня 2018

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

Прежде всего: вы почти никогда не хотите использовать new Task.Это просто означает «сделать задачу, которая представляет эту работу». Это не значит выполнять эту работу .new Task составляет список дел;он не выполняет всю работу по списку!

Во-вторых, вы почти никогда не захотите использовать Task.Run.Это означает создание задачи, представляющей работу , и назначение работника из пула для ее выполнения .Вы не хотите выделять работника, если только выполняемая вами работа не является синхронной и привязкой к процессору , которой ваша работа не является.

В-третьих, выпочти никогда не хотите использовать что-то, что уже является задачей .У вас в руках асинхронная лямбда. Возвращает задание при вызове, поэтому, если вы хотите задание для работающего рабочего процесса, вызывайте лямбду!

В-четвертых, вы почти никогда не захотите WaitAll. Это разрушает весь смысл асинхронности, превращая асинхронный рабочий процесс обратно в синхронный рабочий процесс .

В-пятых, вы почти никогда не хотите вызывать Wait для задачи по той же причине.Но это становится хуже!Вот список дел, который я хотел бы, чтобы вы сделали: во-первых, положите хлеб в тостер и начинайте его жарить.Во-вторых, синхронно ждать, пока бутерброд будет готово;не двигайтесь дальше, пока этот шаг не будет завершен.В-третьих, есть бутерброд.В-четвертых, когда тостер выскочит, выньте тост из тостера и положите на него немного ветчины, чтобы сделать бутерброд. Вы будете ждать вечно, если попытаетесь выполнить этот рабочий процесс .Асинхронные рабочие процессы тупик когда вы вставляете в них синхронные ожидания , потому что вы часто находитесь в ситуации, когда вы ожидаете работу, которую вы будете выполнять в будущем .

(Помимо этого последнего пункта: если вы находитесь в такой ситуации, правильное решение - НЕ , чтобы изменить второй шаг рабочего процесса на «нанять работника для завершения моего сэндвича и синхронно ждатьэтого работника нужно сделать ". Вы часто видите такого рода странное расточительное исправление неверного рабочего процесса. Когда вы удаляете синхронные ожидания и вставляете асинхронные ожидания (await) в точках, где рабочий процесс не может продолжаться до тех пор, пока задача не будет завершена.завершив, вы обнаружите, что все ваши рабочие процессы могут выполняться одним потоком .)

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

ОК, теперь, когда вы знаете, как этого не делать,как вы это делаете?

  • Если у вас есть метод или лямбда, который возвращает задачу, вызовите ее, чтобы получить задачу .
  • Если вам нужнодождитесь завершения этой задачи, прежде чем продолжить ваш рабочий процесс, await задачу.
  • Если у вас есть несколько задач, и вам нужно выполнить их все, прежде чем ваш рабочий процесс можно будет продолжить, поместите их в коллекциюи затем вызовите WhenAll, чтобы получить новую задачу, которая представляет собой задачу завершения всех этих задач .Затем вы должны await выполнить задачу .

Некоторые правильные рабочие процессы:

Вот самый простой из них:

private async Task GetStuffAsync()
{
    // same work here
    await MethodInsideThatNeedsAwaitingAsync();
    // more work
}
private async Task DoItAsync()
{
    // do work
    await GetStuffAsync();
    // do more work
}

Что еслиу вас есть несколько задач, и вы хотите ждать их всех, но они не должны ждать друг друга?

private async Task DoItAsync()
{
    // do work
    Task t1 = GetStuffAsync();
    // do work
    Task t2 = GetOtherStuffAsync();
    // do more work
    // We cannot continue until both are done
    await t1;
    await t2;
    // do even more work
}

Что если у вас есть неизвестное число такихзадачи?

private async Task DoItAsync()
{
    // do work
    var tasks = new List<Task>();
    while (whatever)
      tasks.Add(GetStuffAsync()); 
    // do work
    // we cannot continue until all tasks are done
    await Task.WhenAll(tasks);
    // do more work
}
...