如何启动异步任务对象

时间:2018-10-11 16:16:31

标签: 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(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

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

2 个答案:

答案 0 :(得分:6)

问题是您使用的<div> <h1 id="test-h1">Line height 0, reported height 0:</h1> <pre id="bounding-rect"></pre> </div>构造函数接受p = subprocess.Popen(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell = True) while p.poll() is None: line = p.stdout.readline() if "somestring" in line: functionCall() 委托。当你说

Task

您不是要创建“做事”的任务;相反,您创建了一个任务,该任务执行返回任务的操作,并且那个(内部)任务就是“做事”。创建此(内部)任务非常快,因为委托人在第一个Action处返回var task = new Task(async () => { /* Do things */ }); ,几乎立即完成。由于委托是Task,因此有效的await被丢弃,现在不再可以等待。

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

有一些构造函数重写,可以让您做自己想做的事情,但是语法会像Paulo的回答那样麻烦一些:

Task

您现在有一个执行相同任务的任务,但是这次返回内部await Task.WhenAll(tasks)。要等待内部任务,您可以执行以下操作:

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或只是调用任务返回的更标准的做法方法不符合要求(它将在99.99%的时间内完成)。

这些await Task.WhenAll(await Task.WhenAll(taskList)); 构造函数中的大多数是在异步等待之前就创建的,并且可能由于传统原因仍然存在。


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

对于Task.Run(),我们有

Task

(此处的主要问题是new Task(async () => { /* Do things */ })

对于async void Action() { } ,我们有

void

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

答案 1 :(得分:2)

我不知道为什么找到Sub。只是不需要在代码中进行任何使用。

我还想知道您在哪里读到推荐使用void构造函数。

无论如何,不​​使用Task.Run构造函数,将是这样的:

Task

然后输出:

Task

使用class TaskTest { private async Task CreatePauseTask(int ms) { 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) }); await Task.WhenAll(taskList); Console.WriteLine("------------"); } } static void Main() { var t = new TaskTest(); t.Start(); Console.ReadKey(); } 构造函数将是:

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

然后输出:

Task

这里的问题是class TaskTest { private 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"); }, TaskCreationOptions.DenyChildAttach); } public async Task Start() { var taskList = new List<Task>(new[] { CreatePauseTask(1000), CreatePauseTask(2000) }); taskList.ForEach(t => t.Start(TaskScheduler.Default)); await Task.WhenAll(taskList); Console.WriteLine("------------"); } } static void Main() { var t = new TaskTest(); t.Start(); Console.ReadKey(); } 理解Start 1000 ms pause Start 2000 ms pause ------------ 1000 ms are elapsed 2000 ms are elapsed 返回的函数,而Task.Run构造函数却不知道。因此,使用Task构造函数将调用该函数,该函数将在第一次阻塞等待(Task)时返回。

这是预期的行为。