如何等待以后开始的任务

本文关键字:开始 任务 何等待 等待 | 更新日期: 2023-09-27 18:06:19

在我的代码示例中,主线程不会在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开始
<主要结束/strong>
task2结束

如何更改'主端'将位于列表末尾的代码

如何等待以后开始的任务

getTask2中,您启动的Task只是启动另一个Task。因此,当您await Task时,它将在完成启动内部任务后立即继续执行,而不是当内部任务完成时。

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

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

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的lambda,它将自动为您展开值:

private Task getTask2()
{
    return Task.Run(async () =>
    {
        Console.WriteLine("task2 start");
        await Task.Delay(100);
        Console.WriteLine("task2 end");
    });
}
再一次,这都是假设只是开始异步操作需要卸载到另一个线程,这应该是非常罕见。通常这意味着在异步方法中有一个bug,因为它不应该在给你Task之前做长时间运行的同步工作,但有时该方法将来自一个你无法控制的外部库,所以这将是你唯一的选择。

您正在使用的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将在该点异步继续,这解释了为什么"Main end"在"task2 end"之前打印。

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

要解决此问题,需要使用理解异步操作的方法启动任务。一个异步操作看起来像这样:Func<Task> .

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

public static Task Run(Func<Task> function)

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

如果你想延迟第二个"任务"的执行,那么我建议让getTask2方法返回Func<Task>(异步操作)而不是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");
}

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();

您的代码中有错误。您还没有用关键字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()语句。await关键字已经暗示了这一点。