如何启动异步任务对象

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)

有人能告诉我我的错误是什么以及如何解决它?

sel*_*ape 6

问题是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在语法上是相同的,但在语义上是不同的.

  • 你也可以写“await Task.WhenAll(taskList.Select(outer =&gt;outer.Unwrap()));” (2认同)