控制流在返回结果后如何保持在异步方法范围内?这类似于变量的闭包吗

本文关键字:类似于 变量 闭包 异步方法 结果 返回 控制流 何保持 范围内 | 更新日期: 2023-09-27 17:50:35

在下面的代码中,在"几乎完成"之前将42打印到控制台。由于42已经返回,控件如何仍处于AsyncCheck中?我知道,如果任务在"等待"时没有完成,它会返回控制,但这里我有一个明确的返回语句,所以我很难理解这一点。

async static Task<int> AsyncCheck()
{
   await Task.Factory.StartNew( async () => 
    {
        Console.WriteLine("Start awesome work");
        await Task.Delay(1000);
        Console.WriteLine("Almost done");
        await Task.Delay(1000);
        Console.WriteLine("Ok, done!");
    });
   return 42;
}
async static void AsyncCall()
{
    int result = await AsyncCheck();
    Console.WriteLine(result);
}

我使用"async void"来检查这个示例。

控制流在返回结果后如何保持在异步方法范围内?这类似于变量的闭包吗

您不应该将Task.Factory.StartNewasync-await一起使用。您应该使用Task.RunTask.Factory.StartNew出现在.Net 4.0中时没有考虑到async-await。你仍然可以使用它,但除非你有充分的理由,Task.Run是一个更安全的选择。

它没有返回Task,而是返回一个Task<Task>,您需要在等待之前打开它:

Task<Task> task = Task.Factory.StartNew( async () => 
{
    Console.WriteLine("Start awesome work");
    await Task.Delay(1000);
    Console.WriteLine("Almost done");
    await Task.Delay(1000);
    Console.WriteLine("Ok, done!");
});
Task unwrappedTask = task.Unwrap();
await unwrappedTask

如果没有它,你就不会真正等待lambda完成。您所要做的就是使用一个ThreadPool线程来启动这个异步方法。

基本上,当代码流继续运行时,您在另一个线程上并行执行lambda。这就是为什么您在"几乎完成">

之前看到"42">

让我们放松!你有

async static Task<int> AsyncCheck()
{
   await Task.Factory.StartNew( async () => 
    {
        Console.WriteLine("Start awesome work");
        await Task.Delay(1000);
        Console.WriteLine("Almost done");
        await Task.Delay(1000);
        Console.WriteLine("Ok, done!");
    });
   return 42;
}
async static void AsyncCall()
{
    int result = await AsyncCheck();
    Console.WriteLine(result);
}

编译器将其转换为以下内容:

static Task<int> AsyncCheck()
{
   Task t = Task.Factory.StartNew( () => 
    {
        Console.WriteLine("Start awesome work");
        Task d1 = Task.Delay(1000);
        d1.ContinueWith(t1 => {
          Console.WriteLine("Almost done");
          Task t2 = Task.Delay(1000);
          t2.ContinueWith( tt2 => {
            Console.WriteLine("Ok, done!");
          }
        }
    });
   //no dependecies on t, so just return straight away
   return Task.FromResult(42);
}
static void AsyncCall()
{
    Task<int> result = AsyncCheck();
    result.ContinueWith( t => {
      Console.WriteLine(t.Result);
    })
}

一旦任务完成,ContinueWith将运行它的主体。这应该使控制流程清晰。