如何等待以后开始的任务
本文关键字:开始 任务 何等待 等待 | 更新日期: 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在逻辑上等同于另一个Task
的Result
:
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
关键字已经暗示了这一点。