我想同时启动一组 Task
对象并等待所有对象完成。以下代码显示了我想要的行为。
public class Program
{
class TaskTest
{
private Task createPauseTask(int ms)
{
// works well
return Task.Run(async () =>
// subsitution: return new Task(async () =>
{
Console.WriteLine($"Start {ms} ms pause");
await Task.Delay(ms);
Console.WriteLine($"{ms} ms are elapsed");
});
}
public async Task Start()
{
var taskList= new List<Task>(new[]
{
createPauseTask(1000),
createPauseTask(2000)
});
// taskList.ForEach(x => x.Start());
await Task.WhenAll(taskList);
Console.WriteLine("------------");
}
}
public static void Main()
{
var t = new TaskTest();
Task.Run(() => t.Start());
Console.ReadKey();
}
}
输出是:
Start 1000 ms pause
Start 2000 ms pause
1000 ms are elapsed
2000 ms are elapsed
------------
现在我想知道是否可以准备我的任务并单独启动它们。为此,我将 createPauseTask(int ms)
方法的第一行从 return Task.Run(async () =>
更改为 return new Task(异步 () =>
在 Start()
方法中,我在 WhenAll
之前包含了一个 taskList.ForEach(x => x.Start());
>。但随后可怕的事情发生了。 WhenAll
调用立即完成,输出为:
Start 2000 ms pause
Start 1000 ms pause
------------
1000 ms are elapsed
2000 ms are elapsed
有人可以告诉我我的错误是什么以及如何改正吗?
最佳答案
问题是 Task
您正在使用的构造函数接受 Action
代表。当你说
var task = new Task(async () => { /* Do things */ });
你不是在创建一个“做事”的任务;相反,您创建了一个任务,该任务执行返回任务的操作,而该(内部)任务就是“做事”。创建这个(内部)任务非常快,因为委托(delegate)返回 Task
在第一个await
,并几乎立即完成。由于代表是 Action
, 结果 Task
被有效地丢弃,现在不能再被用来等待。
当您调用 await Task.WhenAll(tasks)
时在你的外部任务上,你只是在等待创建内部任务(几乎是立即的)。之后内部任务继续运行。
有一些构造函数重写可以让你做你想做的事,但是你的语法会有点麻烦,就像 Paulo 的回答一样:
public static Task<Task> CreatePauseTask(int ms)
{
return new Task<Task>(async () =>
{
Console.WriteLine($"Start {ms} ms pause");
await Task.Delay(ms);
Console.WriteLine($"{ms} ms are elapsed");
});
}
您现在有一个执行相同操作的任务,但这次返回内部 Task
.要等待内部任务,您可以这样做:
await Task.WhenAll(await Task.WhenAll(taskList));
内部 await 返回内部任务列表,外部 await 返回它们。
不过,正如还提到的那样 - 使用构造函数创建未启动的任务是一个你真正应该进入的领域,如果你有一个非常具体的要求,其中更标准的做法是使用 Task.Run()
或者简单地调用任务返回方法不符合要求(它会在 99.99% 的时间内完成)。
大多数 Task
构造函数是在 async-await 存在之前创建的,并且可能只是由于遗留原因而仍然存在。
编辑:查看 2 位代表的签名可能也有帮助,C# lambda 表示法允许我们(通常很方便)忽略这些签名。
对于 new Task(async () => { /* Do things */ })
我们有
async void Action() { }
(void
是这里的主要问题)。
对于 new Task<Task>(async () => { /* Do things */ })
我们有
async Task Function() { }
这两个 lambda 在句法上相同,但在语义上不同。
关于c# - 如何启动异步任务对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52764678/