运行 列表中的任务

Running Tasks from List

我正在尝试使用以下方法 运行 任务列表:

List<Task> tasks = new List<Task>();

tasks.Add(new Task(() => this.firstMethod()));
tasks.Add(new Task(() => this.secondMethod()));

但是,如果我使用下面两个示例之一,我会遇到以下问题:

foreach (Task task in tasks)
{
    await Task.Run(() => task);
}

在第一种情况下,任务根本 运行。

foreach (Task task in tasks)
{
    task.Start();
    task.Wait();
}

在第二种情况下,它只 运行 一次,然后我得到以下错误:

Start may not be called on a task that has completed

我错过了什么?

这是一个单元测试,展示了如何做到这一点:

    public class TasksTests
    {
        private readonly ITestOutputHelper _output;

        public TasksTests(ITestOutputHelper output)
        {
            _output = output ?? throw new ArgumentNullException(nameof(output));
        }

        [Fact]
        public async Task CanCreateAndRunTasks()
        {
            var tasks = new List<Task>
            {
                new Task(() => _output.WriteLine("Task #1")),
                new Task(() => _output.WriteLine("Task #2"))
            };

            tasks.ForEach(t => t.Start());

            await Task.WhenAll(tasks);
        }
    }

您首先创建任务。然后你需要启动它们。最后,您需要等待所有这些,例如,使用 Task.WhenAll.

第一个案例样本

foreach (Task task in tasks)
{
    await Task.Run(() => task);
}

应该改为

foreach (Task task in tasks)
{
    task.Start(); // there is no sense to await since tasks should be run in parallel I suppose
}

第二种情况还不清楚。 你 运行 第一个案例之后的第二个案例吗?如果你在 Start 调用之前刚刚初始化了 tasks 应该没问题,比如

List<Task> tasks = new List<Task>()
{
new Task(() => this.firstMethod()),
new Task(() => this.secondMethod()),
};
foreach (Task task in tasks)
{
    task.Start();
    task.Wait(); // this causes tasks to be run in sequence (one by one) like with await in the first sample
}

最简单的情况就是使用Task.Run

var tasks = new[Task]
{
Task.Run(firstMethod),
Task.Run(secondMethod),
}; // tasks are started immediately there is no need to start them later

Task.WaitAll(tasks); // wait until all tasks are finished

您不能重复使用任务。那么让我们开始创建一个委托数组

List<Action> tasks = new List<Action>();

tasks.Add(this.firstMethod);
tasks.Add(this.secondMethod);

然后 运行 它们在附加线程上按顺序排列(让主线程自由更新 UI):

foreach (Action task in tasks)
{
    await Task.Run(task);
}

但是这个循环可以通过多种方式完成,具体取决于调用代码的上下文和有效负载的性质。

这可能不是最佳解决方案。