如何等待以后开始的任务

时间:2016-09-09 15:59:28

标签: c# asynchronous async-await

在我的代码示例中,主线程在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");
    });
}

执行此代码的结果

  

主要开始
  task1开始
  task1结束
  主要2   主3   task2开始
  主要目的
  task2结束

如何更改“主要结束”位于列表末尾的代码。

4 个答案:

答案 0 :(得分:2)

getTask2 {} {}}中,您只需启动Task 另一个Task。所以当你await Task它完成开始内部任务后它将继续执行,而不是当内部任务完成时

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

当然,如果由于某种原因,你需要创建一个新的Task只是为了开始一个新任务,那么你需要让内部Task出来确保主方法仅在内部 Task完成后继续执行。创建Unwrap的{​​{1}}方法在逻辑上等同于另一个Task的{​​{1}} Task

Result

但你甚至不需要这样做。如果给出一个产生Task的lambda,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.Run

再一次,这是假设只需启动异步操作需要卸载到另一个线程,这应该非常罕见。通常这意味着该异步方法中存在一个错误,因为它在首先给你Task之前不应该进行长时间的同步工作,但有时该方法将来自一个你无法控制的外部图书馆,所以这是你唯一的选择。

答案 1 :(得分:1)

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

public Task(Action action)

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

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

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

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

要解决此问题,您需要使用了解异步操作的方法来启动任务。异步操作如下所示:Func<Task>

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

public static Task Run(Func<Task> function)

因此,要修复代码,请将return new Task(...更改为return Task.Run(...,然后不要在返回的任务上调用Start方法,因为Task.Run将创建{{1}已经开始了。

如果你想延迟执行第二个“任务”,那么我建议让Task方法返回getTask2(异步操作)而不是Func<Task>,如下所示:

Task

然后在需要private Func<Task> getTask2() { return async () => { Console.WriteLine("task2 start"); await Task.Delay(100); Console.WriteLine("task2 end"); }; } 方法时运行此类异步操作:

Run

答案 2 :(得分:0)

new Task(本身不能与异步函数一起使用。您正在创建Task<Task>,需要在等待内部任务之前调用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();

答案 3 :(得分:-1)

您的代码中有错误。您尚未使用关键字getTask2标记async方法,而是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()声明。这已由await关键字隐含。

希望这有帮助。