【问题标题】:How to wait for later started task如何等待稍后开始的任务
【发布时间】:2016-09-10 03:01:20
【问题描述】:

在我的代码示例中,当 task2 完成时,主线程不会等待。

public async Task Run()
{
    Console.WriteLine("Main start");
    await getTask1();
    Console.WriteLine("Main 2");
    var task2 = getTask2();
    await Task.Delay(1);
    Console.WriteLine("Main 3");
    task2.Start();
    await task2;
    Console.WriteLine("Main end");
}

private async Task getTask1()
{
    Console.WriteLine("task1 start");
    await Task.Delay(100);
    Console.WriteLine("task1 end");
}

private Task getTask2()
{
    return new Task(async () =>
    {
        Console.WriteLine("task2 start");
        await Task.Delay(100);
        Console.WriteLine("task2 end");
    });
}

这段代码的执行结果是

主要开始
任务1开始
任务1结束
主线 2
主要 3
task2 开始
主要结束
任务2结束

如何更改“Main end”位于列表末尾的代码。

【问题讨论】:

    标签: c# asynchronous async-await


    【解决方案1】:

    getTask2 中,您正在启动一个Task,而它只是启动另一个Task。因此,当您 await Task 时,它会在完成开始内部任务后继续执行,而不是在该内部任务完成时继续执行。

    在这种情况下,您根本没有理由首先创建一个新任务来开始一个新任务;您应该只使用第一种方法中使用的方法。

    当然,如果出于某种原因,您确实需要创建一个新的Task 来启动一个新任务,那么您需要取出内部的Task 并确保main 方法仅在 inner Task 完成后继续执行。有一个Unwrap 方法可以创建一个Task,它在逻辑上等同于一个Task,它是另一个ResultTask

    private Task getTask2()
    {
        Task<Task> task = new Task<Task>(async () =>
        {
            Console.WriteLine("task2 start");
            await Task.Delay(100);
            Console.WriteLine("task2 end");
        });
        task.Start();
        return task.Unwrap();
    }
    

    但您甚至不需要这样做。如果给定一个产生Task 的lambda,Task.Run 将自动为您解开该值:

    private Task getTask2()
    {
        return Task.Run(async () =>
        {
            Console.WriteLine("task2 start");
            await Task.Delay(100);
            Console.WriteLine("task2 end");
        });
    }
    

    再一次,这一切都假设只是开始异步操作需要卸载到另一个线程,这应该非常很少见。通常这意味着该异步方法中存在一个错误,因为它不应该在首先给您Task 之前执行长时间运行的同步工作,但有时该方法将来自您不使用的外部库' t 控制,所以这将是你唯一的选择。

    【讨论】:

    • 我不能使用Task.Run,因为它会在主线程的任何延迟上运行task2。我在示例中添加了await Task.Delay(1); 以显示此行为。
    • 就像我在答案中所说的那样,正确的方法几乎肯定是只做你在第一个解决方案中所做的事情。您几乎可以肯定不应该在这里使用Task.Run,因为您不需要需要将该工作卸载到另一个线程。
    【解决方案2】:

    您正在使用的Task 的构造函数重载具有以下签名:

    public Task(Action action)
    

    虽然您可以将以下表达式分配给Action 类型的变量:

    async () =>
        {
            Console.WriteLine("task2 start");
            await Task.Delay(100);
            Console.WriteLine("task2 end");
        }
    

    此操作将指向一个同步函数,一旦达到await Task.Delay(100); 中的await,该函数将同步完成(尽管“操作”的其余部分将异步继续)。因此,就Task 对象而言,一旦达到此await,就完成了此Action 的执行。这意味着await task2; 中的await 将在该点异步继续,这解释了为什么在“task2 end”之前打印“Main end”。

    所以基本上,Task 的构造函数不支持异步操作,只支持同步操作。

    要解决此问题,您需要使用理解异步操作的方法启动任务。异步操作如下所示:Func&lt;Task&gt;

    Task.Run 方法支持异步操作,因为它具有以下重载:

    public static Task Run(Func<Task> function)
    

    因此,要修复您的代码,请将return new Task(... 更改为return Task.Run(...,然后不要对返回的任务调用Start 方法,因为Task.Run 将创建一个已经启动的Task

    如果你想延迟第二个“任务”的执行,那么我建议让getTask2方法返回Func&lt;Task&gt;(一个异步操作)而不是Task这样:

    private Func<Task> getTask2()
    {
        return async () =>
        {
            Console.WriteLine("task2 start");
            await Task.Delay(100);
            Console.WriteLine("task2 end");
        };
    }
    

    然后在需要时从Run 方法中运行这样的异步操作,如下所示:

    public async Task Run()
    {
        Console.WriteLine("Main start");
        await getTask1();
        Console.WriteLine("Main 2");
        var task2 = getTask2(); //construct asynchronous action
        Console.WriteLine("Main 3");
        await Task.Run(task2); //execute the asynchronous action
        Console.WriteLine("Main end");
    }
    

    【讨论】:

      【解决方案3】:

      new Task( 本身不适用于异步函数。您正在创建一个Task&lt;Task&gt;,您需要在等待内部任务之前调用Unwrap()

      private Task<Task> getTask2()
      {
          return new Task(async () =>
          {
              Console.WriteLine("task2 start");
              await Task.Delay(100);
              Console.WriteLine("task2 end");
          });
      }
      
      var task2 = getTask2();
      Console.WriteLine("Main 3");
      task2.Start();
      await task2.Unwrap();
      

      【讨论】:

        【解决方案4】:

        您的代码中有错误。您尚未使用关键字 async 标记 getTask2 方法,但您 await 其结果。即使您在方法中使用async 委托,也必须使用async 关键字才能成功等待。

        这里有一个关于如何实现你想要的结果的想法。

        public async Task Run()
            {
                Console.WriteLine("Main start");
                await getTask1();
                await getTask2();
                Console.WriteLine("Main end");
            }
        
            public async Task AlternateRun()
            {
                Console.WriteLine("Main start");
                List<Task> task_list = new List<Task>();
                task_list.Add(getTask1());
                task_list.Add(getTask2());
        
                var task_result = Task.WhenAll(task_list);
                task_result.Wait();
                Console.WriteLine("Main end");
            }
        
            private async Task getTask1()
            {
                Console.WriteLine("task1 start");
                await Task.Delay(100);
                Console.WriteLine("task1 end");
            }
        
            private async Task getTask2()
            {
                Console.WriteLine("task2 start");
                await Task.Delay(100);
                Console.WriteLine("task2 end");
            }
        

        我还添加了一个 AlternateRun 方法,该方法将演示等待多个任务的另一种样式。如果您需要按顺序完成任务,请考虑使用ContinueWith 扩展方法。

        请注意,我还删除了您的 task2.Start()statement。 await 关键字已经暗示了这一点。

        希望这会有所帮助。

        【讨论】:

        • 一个方法需要标记async才能等待它。可以等待任何返回 Task 的方法。 (从技术上讲,它甚至不需要是Task,只需一个具有某些属性的对象,Task 就可以。)现在是的,OP 的代码确实有一个逻辑错误,阻止它像他一样运行希望它(显然它编译得很好,仅此就反驳了您的开头段落),但这与您的主张完全无关。
        • OP 在代码 task2.Start() 上有以下几行;等待任务2;并且还表明他/她希望例程以“主结束”结束这表明 OP 正在等待 getTask2 的结果,该结果永远不会按该顺序返回。我非常感谢您指出任何任务都可以等待这一事实,但我会让 OP 判断哪个答案最有可能回答了这个问题。
        • 是的,OP 在他的代码中有一个错误,导致操作执行的顺序与他想要的不同。这并没有改变您的答案在其陈述中完全不正确的事实。 OPs 代码的问题与您声称的问题无关。哦,虽然我们正在这样做,但您关于等待任务开始的说法也是完全错误的。等待一个任务只是向它添加一个延续,导致该方法在该任务完成时继续执行。它与任务最终如何完成无关。
        猜你喜欢
        • 2020-03-15
        • 2019-10-08
        • 2014-06-28
        • 2016-03-06
        • 2016-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多