如何启动异步任务对象 [英] How to start async Task objects

查看:104
本文介绍了如何启动异步任务对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想同时启动Task对象的集合,并等待所有步骤完成.以下代码显示了我想要的行为.

I want to start a collection of Task objects at the same time and wait until all are complete. The following code shows my desired behaviour.

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(async () =>

Now I wonder if it is possible to prepare my tasks and start them separately. In order to do that I change the first line in createPauseTask(int ms)method from return Task.Run(async () => to return new Task(async () =>

,在Start()方法中,我在WhenAll之前加入了taskList.ForEach(x => x.Start());.但是随后发生了可怕的事情. WhenAll调用立即完成,输出为:

and in the Start() method I included a taskList.ForEach(x => x.Start()); before the WhenAll. But then terrible things happen. The WhenAll call completes immediately and the output is:

Start 2000 ms pause
Start 1000 ms pause    
------------    
1000 ms are elapsed
2000 ms are elapsed

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

Can someone show me what my mistake is and how to fix it?

推荐答案

问题是您使用的Task构造函数接受Action委托.当你说

The issue is that the Task constructor you're using accepts an Action delegate. When you say

var task = new Task(async () => { /* Do things */ });

您没有创建做事"的任务;相反,您创建了一个任务,该任务执行返回任务的操作,并且那个(内部)任务就是做事".创建此(内部)任务非常快,因为委托人在第一个await处返回Task,并几乎立即完成.由于委托是Action,因此结果Task被有效地丢弃,现在不再可以等待使用.

you are not creating a task that "does things"; you are instead created a task that executes an action that returns a task, and that (inner) task is what "does things". Creating this (inner) task is very quick, as the delegate returns the Task at the first await, and completes almost immediately. As the delegate is an Action, the resultant Task is effectively discarded, and can now no-longer be used to be awaited.

当您对外部任务调用await Task.WhenAll(tasks)时,您仅在等待内部任务的创建(几乎立即).内部任务随后将继续运行.

When you call await Task.WhenAll(tasks) on your outer tasks, you are only waiting for the inner tasks to be created (almost immediate). The inner tasks continue running afterwards.

有一些构造函数重写,可以让您做自己想做的事,但是语法上却像Paulo的回答那样繁琐:

There are constructor overrides which allow you to do what you want, but your syntax will be a little more cumbersome, along the lines of Paulo's answer:

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.要等待内部任务,您可以执行以下操作:

You now have a task that does the same, but this time returns the inner Task. To await the inner tasks, you can do this:

await Task.WhenAll(await Task.WhenAll(taskList));

内部等待返回内部任务的列表,而外部等待等待的任务列表.

The inner await returns the list of inner tasks, and the outer await await's them.

但是,也已经提到-使用构造函数创建未启动的任务是您真正应该进入的领域,如果您有一个高度特定的要求,那么使用Task.Run()或直接调用任务返回方法的更标准的做法确实会不符合要求(它将在99.99%的时间内完成).

As also mentioned, though - creating unstarted tasks using constructors is an area you really should only be in if you have a highly-specific requirement where the more-standard practice of using Task.Run() or simply calling task-returning methods does not meet the requirement (which it will do 99.99% of the time).

大多数Task构造函数都是在异步等待之前创建的,并且由于传统原因可能仍然存在.

Most of those Task constructors were created before async-await even existed, and are probably only still there for legacy reasons.

编辑:也许还可以帮助查看2个委托的签名,C#lambda表示法使我们可以(通常很方便)忽略它们.

Edit: what might also help is to see the signatures of the 2 delegates, which the C# lambda notation allows us to - usually conveniently - overlook.

对于new Task(async () => { /* Do things */ }),我们有

async void Action() { }

(这里的主要问题是void).

(the void being the main issue here).

对于new Task<Task>(async () => { /* Do things */ }),我们有

async Task Function() { }

两个lambda在语法上是相同的,但在语义上是不同的.

Both lambdas are syntactically identical, yet semantically different.

这篇关于如何启动异步任务对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆