c# - 如何启动异步任务对象

标签 c# async-await task

我想同时启动一组 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/

相关文章:

c# - netstandard20 中的 System.Data.Linq

c# - 使用 Xamarin Forms 播放视频

c# - 上次。等待与线程一起工作吗?

c# - TaskFactory.StartNew -> System.OutOfMemoryException

ruby-on-rails - Ruby:在没有 Rails 的情况下从 gem 访问 rake 任务

c# - PInvoke 整数编码返回不正确的结果

c# - 通过ajax将模型发布到 Controller 为空

javascript - 'await' 对此表达式的类型没有影响

python - 如何在Python中从 future 检索任务?

task - VSCode : how does tasks work?