• C#多线程异步返回类型


    异步返回类型 (C#)

    •  

      异步方法可以具有以下返回类型:

      有关异步方法的详细信息,请参阅使用 Async 和 Await 的异步编程 (C#)

      在以下其中一节检查每个返回类型,且在本主题末尾可以找到使用全部三种类型的完整示例。

      Task<TResult> 返回类型

      Task<TResult> 返回类型用于某种异步方法,此异步方法包含 return (C#) 语句,其中操作数具有类型 TResult

      在下面的示例中,GetLeisureHours 异步方法包含返回整数的 return 语句。 因此,该方法声明必须指定 Task<int> 的返回类型。 FromResult 异步方法是返回字符串的操作的占位符。

      C#复制

      using System;
      using System.Linq;
      using System.Threading.Tasks;
      
      public class Example
      {
         public static void Main()
         {
            Console.WriteLine(ShowTodaysInfo().Result);
         }
      
         private static async Task<string> ShowTodaysInfo()
         {
            string ret = $"Today is {DateTime.Today:D}\n" +
                         "Today's hours of leisure: " +
                         $"{await GetLeisureHours()}";
            return ret;
         }
      
         static async Task<int> GetLeisureHours()  
         {  
             // Task.FromResult is a placeholder for actual work that returns a string.  
             var today = await Task.FromResult<string>(DateTime.Now.DayOfWeek.ToString());  
           
             // The method then can process the result in some way.  
             int leisureHours;  
             if (today.First() == 'S')  
                 leisureHours = 16;  
             else  
                 leisureHours = 5;  
           
             return leisureHours;  
         }  
      }
      // The example displays output like the following:
      //       Today is Wednesday, May 24, 2017
      //       Today's hours of leisure: 5
      // </Snippet >
      
      

      在 ShowTodaysInfo 方法中从 await 表达式内调用 GetLeisureHours 时,await 表达式检索存储在由 GetLeisureHours 方法返回的任务中的整数值(leisureHours 的值)。 有关 await 表达式的详细信息,请参阅 await

      通过从应用程序 await 中分离对 GetLeisureHours 的调用,你可以更好地了解此操作,如下面的代码所示。 对非立即等待的方法 GetLeisureHours 的调用返回 Task<int>,正如你从方法声明预料的一样。 该任务指派给示例中的 integerTask 变量。 因为 integerTask 是 Task<TResult>,所以它包含类型 TResult 的 Result 属性。 在这种情况下,TResult 表示整数类型。 await 应用于 integerTask,await 表达式的计算结果为 integerTask 的 Result 属性内容。 此值分配给 ret 变量。

       重要

      Result 属性为阻止属性。 如果你在其任务完成之前尝试访问它,当前处于活动状态的线程将被阻止,直到任务完成且值为可用。 在大多数情况下,应通过使用 await 访问此值,而不是直接访问属性。 
      上一示例通过检索 Result 属性的值来阻止主线程,从而使 ShowTodaysInfo 方法可在应用程序结束之前完成执行。

      C#复制

      var integerTask = GetLeisureHours();
      
      // You can do other work that does not rely on integerTask before awaiting.
      
      string ret = $"Today is {DateTime.Today:D}\n" +
                   "Today's hours of leisure: " +
                   $"{await integerTask}";
      

      任务返回类型

      不包含 return 语句的异步方法或包含不返回操作数的 return 语句的异步方法通常具有返回类型 Task。 如果此类方法同步运行,它们将返回 void。 如果在异步方法中使用 Task 返回类型,调用方法可以使用 await 运算符暂停调用方的完成,直至被调用的异步方法结束。

      如下示例中,WaitAndApologize 异步方法不包含 return 语句,因此此方法返回 Task 对象。 通过这样可等待 WaitAndApologize。 请注意,Task 类型不包含 Result 属性,因为它不具有任何返回值。

      C#复制

      using System;
      using System.Threading.Tasks;
      
      public class Example
      {
         public static void Main()
         {
            DisplayCurrentInfo().Wait();
         }
      
         static async Task DisplayCurrentInfo()
         {
            await WaitAndApologize();
            Console.WriteLine($"Today is {DateTime.Now:D}");
            Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
            Console.WriteLine("The current temperature is 76 degrees.");
         }
      
         static async Task WaitAndApologize()
         {
            // Task.Delay is a placeholder for actual work.  
            await Task.Delay(2000);  
            // Task.Delay delays the following line by two seconds.  
            Console.WriteLine("\nSorry for the delay. . . .\n");  
         }
      }
      // The example displays the following output:
      //       Sorry for the delay. . . .
      //       
      //       Today is Wednesday, May 24, 2017
      //       The current time is 15:25:16.2935649
      //       The current temperature is 76 degrees.
      
      

      通过使用 await 语句而不是 await 表达式等待 WaitAndApologize,类似于返回 void 的同步方法的调用语句。 Await 运算符的应用程序在这种情况下不生成值。

      如同上一个 Task<TResult> 示例,可以从 await 运算符的应用程序中分离对 WaitAndApologize 的调用,如以下代码所示。 但是,请记住,Task 没有 Result 属性,并且当 await 运算符应用于 Task 时不产生值。

      以下代码将调用 WaitAndApologize 方法和等待此方法返回的任务分离。

      C#复制

      Task wait = WaitAndApologize();
      
      string output = $"Today is {DateTime.Now:D}\n" + 
                      $"The current time is {DateTime.Now.TimeOfDay:t}\n" +
                      $"The current temperature is 76 degrees.\n";
      await wait;
      Console.WriteLine(output);
      

      Void 返回类型

      在异步事件处理程序中使用 void 返回类型,这需要 void 返回类型。 对于事件处理程序以外的不返回值的方法,应返回 Task,因为无法等待返回 void 的异步方法。 这种方法的任何调用方必须能够继续完成,而无需等待调用的异步方法完成,并且调用方必须独立于异步方法生成的任何值或异常。

      返回 void 的异步方法的调用方无法捕获从该方法引发的异常,且此类未经处理的异常可能会导致应用程序故障。 如果返回 Task 或 Task<TResult> 的异步方法中出现异常,此异常将存储于返回的任务中,并在等待该任务时再次引发。 因此,请确保可以产生异常的任何异步方法都具有返回类型 Task或 Task<TResult>,并确保会等待对方法的调用。

      有关如何在异步方法中捕获异常的详细信息,请参阅 try-catch 主题的异步方法中的异常部分。

      以下示例演示异步事件处理程序的行为。 请注意,在本示例代码中,异步事件处理程序必须在完成时通知主线程。 然后,主线程可在退出程序之前等待异步事件处理程序完成。

      C#复制

      using System;
      using System.Threading.Tasks;
      
      public class NaiveButton
      {
          public event EventHandler Clicked;
      
          public void Click()
          {
              Console.WriteLine("Somebody has clicked a button. Let's raise the event...");
              Clicked?.Invoke(this, EventArgs.Empty);
              Console.WriteLine("All listeners are notified.");
          }
      }
      
      public class AsyncVoidExample
      {
          static TaskCompletionSource<bool> tcs;
      
          static async Task Main()
          {
              tcs = new TaskCompletionSource<bool>();
              var secondHandlerFinished = tcs.Task;
      
              var button = new NaiveButton();
              button.Clicked += Button_Clicked_1;
              button.Clicked += Button_Clicked_2_Async;
              button.Clicked += Button_Clicked_3;
      
              Console.WriteLine("About to click a button...");
              button.Click();
              Console.WriteLine("Button's Click method returned.");
      
              await secondHandlerFinished;
          }
      
          private static void Button_Clicked_1(object sender, EventArgs e)
          {
              Console.WriteLine("   Handler 1 is starting...");
              Task.Delay(100).Wait();
              Console.WriteLine("   Handler 1 is done.");
          }
      
          private static async void Button_Clicked_2_Async(object sender, EventArgs e)
          {
              Console.WriteLine("   Handler 2 is starting...");
              Task.Delay(100).Wait();
              Console.WriteLine("   Handler 2 is about to go async...");
              await Task.Delay(500);
              Console.WriteLine("   Handler 2 is done.");
              tcs.SetResult(true);
          }
      
          private static void Button_Clicked_3(object sender, EventArgs e)
          {
              Console.WriteLine("   Handler 3 is starting...");
              Task.Delay(100).Wait();
              Console.WriteLine("   Handler 3 is done.");
          }
      }
      
      // Expected output:
      // About to click a button...
      // Somebody has clicked a button. Let's raise the event...
      //    Handler 1 is starting...
      //    Handler 1 is done.
      //    Handler 2 is starting...
      //    Handler 2 is about to go async...
      //    Handler 3 is starting...
      //    Handler 3 is done.
      // All listeners are notified.
      // Button's Click method returned.
      //    Handler 2 is done.
      

      通用的异步返回类型和 ValueTask<TResult>

      从 C# 7.0 开始,异步方法可返回任何具有可访问的 GetAwaiter 方法的类型。

      Task 和 Task<TResult> 是引用类型,因此,性能关键路径中的内存分配会对性能产生负面影响,尤其当分配出现在紧凑循环中时。 支持通用返回类型意味着可返回轻量值类型(而不是引用类型),从而避免额外的内存分配。

      .NET 提供 System.Threading.Tasks.ValueTask<TResult> 结构作为返回任务的通用值的轻量实现。 要使用 System.Threading.Tasks.ValueTask<TResult> 类型,必须向项目添加 System.Threading.Tasks.Extensions NuGet 包。 如下示例使用 ValueTask<TResult> 结构检索两个骰子的值。

      C#复制

      using System;
      using System.Threading.Tasks;
      
      class Program
      {
         static Random rnd;
         
         static void Main()
         {
            Console.WriteLine($"You rolled {GetDiceRoll().Result}");
         }
         
         private static async ValueTask<int> GetDiceRoll()
         {
            Console.WriteLine("...Shaking the dice...");
            int roll1 = await Roll();
            int roll2 = await Roll();
            return roll1 + roll2; 
         } 
         
         private static async ValueTask<int> Roll()
         {
            if (rnd == null)
               rnd = new Random();
            
            await Task.Delay(500);
            int diceRoll = rnd.Next(1, 7);
            return diceRoll;
         } 
      }
      // The example displays output like the following:
      //       ...Shaking the dice...
      //       You rolled 8
      

      请参阅

    反馈

  • 相关阅读:
    如何识别思科胖瘦AP
    路由器重置用户名密码
    python学习之路day09(黏包、md5和进程守护)
    python学习之路day02(容器类型和循环)
    python学习之路08(正则表达式和网络)
    python学习之路day06(模块+面向对象)
    python学习之路day05(迭代器和生成器)
    python学习之路day04(函数)
    【Linux】ssh远程时如何做到不用输入密码登入
    【中间件】GitLab安装
  • 原文地址:https://www.cnblogs.com/grj001/p/12224948.html
Copyright © 2020-2023  润新知