Chr*_*all 2 c# task async-await
我想同时启动一个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();
}
}
Run Code Online (Sandbox Code Playgroud)
输出是:
Start 1000 ms pause
Start 2000 ms pause
1000 ms are elapsed
2000 ms are elapsed
------------
Run Code Online (Sandbox Code Playgroud)
现在我想知道是否可以准备我的任务并单独启动它们.为了做到这一点,我将createPauseTask(int ms)方法中的第一行更改return Task.Run(async () =>为return new Task(async () =>
并且在Start()方法中我包括了taskList.ForEach(x => x.Start());之前的WhenAll.但后来发生了可怕的事情.该WhenAll呼叫立即完成,输出为:
Start 2000 ms pause
Start 1000 ms pause
------------
1000 ms are elapsed
2000 ms are elapsed
Run Code Online (Sandbox Code Playgroud)
有人能告诉我我的错误是什么以及如何解决它?
问题是Task您正在使用的构造函数接受Action委托.当你说
var task = new Task(async () => { /* Do things */ });
Run Code Online (Sandbox Code Playgroud)
你没有创造一个"做事"的任务; 相反,您创建了一个执行返回任务的操作的任务,而该(内部)任务就是"做事".创建此(内部)任务非常快,因为委托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");
});
}
Run Code Online (Sandbox Code Playgroud)
你现在有一个相同的任务,但这次返回内部Task.要等待内部任务,您可以这样做:
await Task.WhenAll(await Task.WhenAll(taskList));
Run Code Online (Sandbox Code Playgroud)
内部await返回内部任务列表,外部等待等待它们.
正如前面提到的那样 - 使用构造函数创建未启动的任务是一个你真正应该只有的领域,如果你有一个高度特定的要求,那么更标准的使用Task.Run()或简单地调用任务返回方法的做法不符合要求(它将占99.99%的时间).
大多数Task构造函数是在async-await甚至存在之前创建的,并且由于遗留原因可能仍然存在.
编辑:可能也有帮助的是看到2个代表的签名,C#lambda符号允许我们 - 通常很方便 - 忽略.
因为new Task(async () => { /* Do things */ })我们有
async void Action() { }
Run Code Online (Sandbox Code Playgroud)
(这void是主要问题).
因为new Task<Task>(async () => { /* Do things */ })我们有
async Task Function() { }
Run Code Online (Sandbox Code Playgroud)
两个lambda在语法上是相同的,但在语义上是不同的.