Использовать Task.WaitAll() для обработки ожидаемых задач?
В идеале, что я хочу сделать, это задержать задачу с неблокирующим режимом, а затем дождаться завершения всех задач. Я попытался добавить объект задачи, возвращенный Task.Delay, а затем использовать Task.WaitAll, но похоже, что это не поможет. Как мне решить эту проблему?
class Program
{
public static async void Foo(int num)
{
Console.WriteLine("Thread {0} - Start {1}", Thread.CurrentThread.ManagedThreadId, num);
var newTask = Task.Delay(1000);
TaskList.Add(newTask);
await newTask;
Console.WriteLine("Thread {0} - End {1}", Thread.CurrentThread.ManagedThreadId, num);
}
public static List<Task> TaskList = new List<Task>();
public static void Main(string[] args)
{
for (int i = 0; i < 3; i++)
{
int idx = i;
TaskList.Add(Task.Factory.StartNew(() => Foo(idx)));
}
Task.WaitAll(TaskList.ToArray());
}
}
Ответы
Ответ 1
Это то, чего вы пытаетесь достичь?
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
public static async Task Foo(int num)
{
Console.WriteLine("Thread {0} - Start {1}", Thread.CurrentThread.ManagedThreadId, num);
await Task.Delay(1000);
Console.WriteLine("Thread {0} - End {1}", Thread.CurrentThread.ManagedThreadId, num);
}
public static List<Task> TaskList = new List<Task>();
public static void Main(string[] args)
{
for (int i = 0; i < 3; i++)
{
int idx = i;
TaskList.Add(Foo(idx));
}
Task.WaitAll(TaskList.ToArray());
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
}
}
Вывод:
Thread 10 - Start 0
Thread 10 - Start 1
Thread 10 - Start 2
Thread 6 - End 0
Thread 6 - End 2
Thread 6 - End 1
Press Enter to exit...
Ответ 2
То, что нужно знать, состоит в том, что, поскольку Foo является асинхронным, он сам является Задачей. В вашем примере есть задачи, которые просто запускают задачу Foo
, но не ждут ее.
Другими словами, Task.WaitAll(TaskList.ToArray())
просто ждет запуска каждого Task.Delay
, но он не ждет завершения всех этих задач.
Это может быть то, чего вы пытаетесь достичь:
class Program
{
public static async Task Foo(int num)
{
Console.WriteLine("Thread {0} - Start {1}", Thread.CurrentThread.ManagedThreadId, num);
var newTask = Task.Delay(1000);
await newTask;
Console.WriteLine("Thread {0} - End {1}", Thread.CurrentThread.ManagedThreadId, num);
}
public static List<Task> TaskList = new List<Task>();
public static void Main(string[] args)
{
for (int i = 0; i < 3; i++)
{
int idx = i;
Task fooWrappedInTask = Task.Run(() => Foo(idx));
TaskList.Add(fooWrappedInTask);
}
Task.WaitAll(TaskList.ToArray());
Console.WriteLine("Finished waiting for all of the tasks: - Thread {0}", Thread.CurrentThread.ManagedThreadId);
}
}
Я тестировал это, и он производит вывод на консоль, на который вы нацеливаетесь.
Основное отличие здесь в том, что мы вызываем Task.Run
вместо Task.Factory.StartNew
.
У вас может быть Task
, который возвращает Task
, который может даже вернуть еще один Task
. Вы могли бы подумать об этом как о "цепочке" задач.
Task.Run
возвращает a Task
, которые представляют конечную задачу в цепочке. Когда вы ждете его, вы ожидаете завершения каждой ссылки в цепочке задач.
Для сравнения, Task.Factory.StartNew
возвращает задачу, которая представляет первое звено в цепочке. После того, как вы дождались этого, вы остаетесь с остальной цепью, чтобы дождаться. Это прекрасно в подавляющем большинстве случаев, когда Task
возвращает то, что не является другим Task
.