<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      異步編程(async&await)轉載自博客園Jonins的博文

      關于Sync Context 的討論: https://stackoverflow.com/questions/18097471/what-does-synchronizationcontext-do

       

      原文鏈接: http://www.rzrgm.cn/jonins/p/9558275.html

      異步編程(async&await)

       前言

      本來這篇文章上個月就該發布了,但是因為忙 QuarkDoc  一直沒有時間整理,所以耽擱到今天,現在回歸正軌。

      C# 5.0 雖然只引入了2個新關鍵詞:asyncawait。然而它大大簡化了異步方法的編程。

      在 線程池(threadPool)大致介紹了微軟在不同時期使用的不同的異步模式,有3種:

      1.異步模式

      2.基于事件的異步模式

      3.基于任務的異步模式(TAP)

      而最后一種就是利用async和await關鍵字來實現的(TAP是現在微軟極力推崇的一種異步編程方式)。

      但請謹記,asyncawait關鍵字只是編譯器功能。編譯器會用Task類創建代碼。如果不使用這兩個關鍵詞,用C#4.0的Task類同樣可以實現相同的功能,只是沒有那么方便而已。

       

      認識asyncawait

      使用asyncawait關鍵詞編寫異步代碼,具有與同步代碼相當的結構和簡單性,并且摒棄了異步編程的復雜結構。

      但是在理解上剛開始會很不習慣,而且會把一些情況想當然了,而真實情況會相去甚遠(我犯過這樣的錯誤)。所以根據幾個示例一步步理解更加的靠譜些。

      1.一個簡單的同步方法

      這是一個簡單的同步方法調用示例:

       1     class Program
       2     {
       3         static void Main(string[] args)
       4         {
       5             Console.WriteLine($"頭部已執行,當前主線程Id為:{Thread.CurrentThread.ManagedThreadId}");
       6             string result = SayHi("jack");
       7             Console.WriteLine(result);
       8             Console.WriteLine($"尾部已執行,當前主線程Id為:{Thread.CurrentThread.ManagedThreadId}");
       9             Console.ReadKey();
      10         }
      11         static string SayHi(string name)
      12         {
      13             Task.Delay(2000).Wait();//異步等待2s
      14             Console.WriteLine($"SayHi執行,當前線程Id為:{Thread.CurrentThread.ManagedThreadId}");
      15             return $"Hello,{name}";
      16         }
      17     }

      執行結果如下,方法在主線程中運行,主線程被阻塞。

      2.同步方法異步化

      示例將方法放到任務內執行:

       1     class Program
       2     {
       3         static void Main(string[] args)
       4         {
       5             Console.WriteLine($"頭部已執行,當前主線程Id為:{Thread.CurrentThread.ManagedThreadId}");
       6             string result = SayHiAsync("jack").Result;
       7             Console.WriteLine(result);
       8             Console.WriteLine($"尾部已執行,當前主線程Id為:{Thread.CurrentThread.ManagedThreadId}");
       9             Console.ReadKey();
      10         }
      11         static Task<string> SayHiAsync(string name)
      12         {
      13             return Task.Run<string>(() => { return SayHi(name); });
      14         }
      15         static string SayHi(string name)
      16         {
      17             Task.Delay(2000).Wait();//異步等待2s
      18             Console.WriteLine($"SayHi執行,當前線程Id為:{Thread.CurrentThread.ManagedThreadId}");
      19             return $"Hello,{name}";
      20         }
      21     }

      執行結果如下,方法在另外一個線程中運行,因為主線程調用了Result,Result在任務沒有完成時內部會使用Wait,所以主線程還是會被阻塞。

      3.延續任務

      示例為了避免阻塞主線程使用任務延續的方式:

       1     class Program
       2     {
       3         static void Main(string[] args)
       4         {
       5             Console.WriteLine($"頭部已執行,當前主線程Id為:{Thread.CurrentThread.ManagedThreadId}");
       6             Task<string> task = SayHiAsync("jack");
       7             task.ContinueWith(t =>//延續任務,指定任務執行完成后延續的操作
       8             {
       9                 Console.WriteLine($"延續執行,當前線程Id為:{Thread.CurrentThread.ManagedThreadId}");
      10                 string result = t.Result;
      11                 Console.WriteLine(result);
      12             });
      13             Console.WriteLine($"尾部已執行,當前主線程Id為:{Thread.CurrentThread.ManagedThreadId}");
      14             Console.ReadKey();
      15         }
      16         static Task<string> SayHiAsync(string name)
      17         {
      18             return Task.Run<string>(() => { return SayHi(name); });
      19         }
      20         static string SayHi(string name)
      21         {
      22             Task.Delay(2000).Wait();//異步等待2s
      23             Console.WriteLine($"SayHi執行,當前線程Id為:{Thread.CurrentThread.ManagedThreadId}");
      24             return $"Hello,{name}";
      25         }
      26     }

      執行結果如下,方法在另外一個線程中運行,因為任務附加了延續,延續會在任務完成后處理返回值,而主線程不會被阻塞。這應該就是想要的效果了。

       

      4.使用async和await構建異步方法調用

       1     class Program
       2     {
       3         static void Main(string[] args)
       4         {
       5             Console.WriteLine($"頭部已執行,當前主線程Id為:{Thread.CurrentThread.ManagedThreadId}");
       6             CallerWithAsync("jack");
       7             Console.WriteLine($"尾部已執行,當前主線程Id為:{Thread.CurrentThread.ManagedThreadId}");
       8             Console.ReadKey();
       9         }
      10         async static void CallerWithAsync(string name)
      11         {
      12             Console.WriteLine($"異步調用頭部執行,當前線程Id為:{Thread.CurrentThread.ManagedThreadId}");
      13             string result = await SayHiAsync(name);
      14             Console.WriteLine($"異步調用尾部執行,當前線程Id為:{Thread.CurrentThread.ManagedThreadId}");
      15             Console.WriteLine(result);
      16         }
      17         static Task<string> SayHiAsync(string name)
      18         {
      19             return Task.Run<string>(() => { return SayHi(name); });
      20         }
      21         static string SayHi(string name)
      22         {
      23             Task.Delay(2000).Wait();//異步等待2s
      24             Console.WriteLine($"SayHi執行,當前線程Id為:{Thread.CurrentThread.ManagedThreadId}");
      25             return $"Hello,{name}";
      26         }
      27     }

      執行結果如下,使用await關鍵字來調用返回任務的異步方法SayHiAsync,而使用await需要有用async修飾符聲明的方法,在SayHiAsync方法為完成前,下面的方法不會繼續執行。但是主線程并沒有阻塞,且任務處理完成后await后的邏輯繼續執行。

      本質:編譯器將await關鍵字后的所有代碼放進了延續(ContinueWith)方法的代碼塊中來轉換await關鍵詞。

       

      解析asyncawait

      1.異步(async)

      使用async修飾符標記的方法稱為異步方法,異步方法只可以具有以下返回類型:
      1.Task
      2.Task<TResult>
      3.void
      4.從C# 7.0開始,任何具有可訪問的GetAwaiter方法的類型System.Threading.Tasks.ValueTask<TResult> 類型屬于此類實現(需向項目添加System.Threading.Tasks.Extensions NuGet 包)。

      異步方法通常包含 await 運算符的一個或多個實例,但缺少 await 表達式也不會導致生成編譯器錯誤。 如果異步方法未使用 await 運算符標記暫停點,那么異步方法會作為同步方法執行,即使有 async 修飾符也不例外,編譯器將為此類方法發布一個警告。

      2.等待(await)

      await 表達式只能在由 async 修飾符標記的封閉方法體lambda 表達式或異步方法中出現。在其他位置,它會解釋為標識符。

      使用await運算符的任務只可用于返回 TaskTask<TResult> System.Threading.Tasks.ValueType<TResult> 對象的方法。

      異步方法同步運行,直至到達其第一個 await 表達式,此時await在方法的執行中插入掛起點,會將方法掛起直到所等待的任務完成,然后繼續執行await后面的代碼區域。

      await 表達式并不阻止正在執行它的線程。 而是使編譯器將剩下的異步方法注冊為等待任務的延續任務。 控制權隨后會返回給異步方法的調用方。 任務完成時,它會調用其延續任務,異步方法的執行會在暫停的位置處恢復。

      注意:

      1.無法等待具有 void 返回類型的異步方法,并且無效返回方法的調用方捕獲不到異步方法拋出的任何異常

      2.異步方法無法聲明 in、ref 或 out 參數,但可以調用包含此類參數的方法。 同樣,異步方法無法通過引用返回值,但可以調用包含 ref 返回值的方法。

       

      異步方法運行機理(控制流)

      異步編程中最需弄清的是控制流是如何從方法移動到方法的。

      下列示例及說明引自(官方文檔),個人認為已經很清晰了:

       1     class Program
       2     {
       3         static void Main(string[] args)
       4         {
       5             var result = AccessTheWebAsync();
       6             Console.ReadKey();
       7         }
       8         async static Task<int> AccessTheWebAsync()
       9         {
      10             HttpClient client = new HttpClient();
      11             // GetStringAsync返回一個任務。任務Result會得到一個字符串(urlContents)。
      12             Task<string> getStringTask = client.GetStringAsync("http://www.rzrgm.cn/jonins/");
      13             //您可以在這里完成不依賴于GetStringAsync的字符串的工作。
      14             DoIndependentWork();
      15             //等待的操作員暫停進入WebAsync。
      16             //AccessTheWebAsync在getStringTask完成之前不能繼續。
      17             //同時,控制權返回到AccessTheWebAsync的調用方。
      18             //當getStringTask完成后,控件權將繼續在這里工作。 然后,await運算符從getStringTask檢索字符串結果。 
      19             string urlContents = await getStringTask;
      20             //任務完成
      21             Console.WriteLine(urlContents.Length);
      22             //return語句指定一個整數結果。 
      23             return urlContents.Length;
      24         }
      25         static void DoIndependentWork()
      26         {
      27             Console.WriteLine("Working..........");
      28         }
      29     }

       

      多個異步方法

      在一個異步方法里,可以調用一個或多個異步方法,如何編碼取決于異步方法間結果是否相互依賴

      1.順序調用異步方法

      使用await關鍵詞可以調用每個異步方法,如果一個異步方法需要使用另一個異步方法的結果,await關鍵詞就非常必要。

      示例如下:

       1     class Program
       2     {
       3         static void Main(string[] args)
       4         {
       5             Console.WriteLine("執行前.....");
       6             GetResultAsync();
       7             Console.WriteLine("執行中.....");
       8             Console.ReadKey();
       9         }
      10         async static void GetResultAsync()
      11         {
      12             var number1 = await GetResult(10);
      13             var number2 =  GetResult(number1);
      14             Console.WriteLine($"結果分別為:{number1}和{number2.Result}");
      15         }
      16         static Task<int> GetResult(int number)
      17         {
      18             return Task.Run<int>(() => { Task.Delay(1000).Wait(); return number + 10; });
      19         }
      20     }

      2.使用組合器

      如果異步方法間相互不依賴,則每個異步方法都不使用await,而是把每個異步方法的結果賦值給Task變量,就會運行得更快。

      示例如下:

       1     class Program
       2     {
       3         static void Main(string[] args)
       4         {
       5             Console.WriteLine("執行前.....");
       6             GetResultAsync();
       7             Console.WriteLine("執行中.....");
       8             Console.ReadKey();
       9         }
      10         async static void GetResultAsync()
      11         {
      12             Task<int> task1 = GetResult(10);
      13             Task<int> task2 = GetResult(20);
      14             await Task.WhenAll(task1, task2);
      15             Console.WriteLine($"結果分別為:{task1.Result}和{task2.Result}");
      16         }
      17         static Task<int> GetResult(int number)
      18         {
      19             return Task.Run<int>(() => { Task.Delay(1000).Wait(); return number + 10; });
      20         }
      21     }

      Task類定于2個組合器分別為:WhenAllWhenAny

      WhenAll是在所有傳入的任務都完成時才返回Task

      WhenAny是在傳入的任務其中一個完成就會返回Task

       

      異步方法的異常處理

      1.異常處理

      以下示例一種是普通的錯誤的捕獲方式,另一種是異步方法異常捕獲方式:

       1     class Program
       2     {
       3         static void Main(string[] args)
       4         {
       5 
       6             DontHandle();
       7             HandleError();
       8             Console.ReadKey();
       9         }
      10         //錯誤處理
      11         static void DontHandle()
      12         {
      13             try
      14             {
      15                 var task = ThrowAfter(0, "DontHandle Error");
      16             }
      17             catch (Exception ex)
      18             {
      19 
      20                 Console.WriteLine(ex.Message);
      21             }
      22         }
      23         //異步方法錯誤處理
      24         static async void HandleError()
      25         {
      26             try
      27             {
      28                 await ThrowAfter(2000, "HandleError Error");
      29             }
      30             catch (Exception ex)
      31             {
      32 
      33                 Console.WriteLine(ex.Message);
      34             }
      35         }
      36         //在延遲后拋出異常
      37         static async Task ThrowAfter(int ms, string message)
      38         {
      39             await Task.Delay(ms);
      40             throw new Exception(message);
      41         }
      42     }

      執行結果如下:

      調用異步方法,如果只是簡單的放在try/catch塊中,將會捕獲不到異常這是因為DontHandle方法在ThrowAfter拋出異常之前已經執行完畢(返回void的異步方法不會等待。這是因為從async void方法拋出的異常無法捕獲。因此異步方法最好返回一個Task類型)。

      異步方法的一個較好異常處理方式是使用await關鍵字,將其放在try/catch中

      2.多個異步方法異常處理

      如果調用了多個異步方法,在第一個異步方法拋出異常,后續的方法將不會被調用,catch塊內只會處理出現的第一個異常。

      所以正確的做法是使用Task.WhenAll,不管任務是否拋出異常都會等到所有任務完成。Task.WhenAll結束后,異常被catch語句捕獲到。如果只是捕獲Exception,我們只能看到WhenAll方法的第一個發生異常的任務信息,不會拋出后續的異常任務

      如果要捕獲所有任務的異常信息,就是對任務聲明變量,在catch塊內可以訪問,再使用IsFaulted屬性檢查任務的狀態,以確認它們是否出現錯誤,然后再進行處理。示例如下:

       1     class Program
       2     {
       3         static void Main(string[] args)
       4         {
       5             HandleError();
       6             Console.ReadKey();
       7         }
       8         //正確的處理方式
       9         static async void HandleError()
      10         {
      11             Task t1 = null;
      12             Task t2 = null;
      13             try
      14             {
      15                 t1 = ThrowAfter(1000, "HandleError-One-Error");
      16                 t2 = ThrowAfter(2000, "HandleError-Two-Error");
      17                 await Task.WhenAll(t1, t2);
      18             }
      19             catch (Exception)
      20             {
      21                 if (t1.IsFaulted)
      22                     Console.WriteLine(t1.Exception.InnerException.Message);
      23                 if (t2.IsFaulted)
      24                     Console.WriteLine(t2.Exception.InnerException.Message);
      25             }
      26         }
      27         //在延遲后拋出異常
      28         static async Task ThrowAfter(int ms, string message)
      29         {
      30             await Task.Delay(ms);
      31             throw new Exception(message);
      32         }
      33     }

      3.使用AggregateException捕獲異步方法異常

       在 任務(task)中介紹過AggregateException,它包含了等待中所有異常的列表,可輕松遍歷處理所有異常信息。示例如下:

       1     class Program
       2     {
       3         static void Main(string[] args)
       4         {
       5             HandleError();
       6             Console.ReadKey();
       7         }
       8         //正確的處理方式
       9         static async void HandleError()
      10         {
      11             Task taskResult = null;
      12             try
      13             {
      14                 Task t1 = ThrowAfter(1000, "HandleError-One-Error");
      15                 Task t2 = ThrowAfter(2000, "HandleError-Two-Error");
      16                 await (taskResult = Task.WhenAll(t1, t2));
      17             }
      18             catch (Exception)
      19             {
      20                 foreach (var ex in taskResult.Exception.InnerExceptions)
      21                 {
      22                     Console.WriteLine(ex.Message);
      23                 }
      24           
      25             }
      26         }
      27         //在延遲后拋出異常
      28         static async Task ThrowAfter(int ms, string message)
      29         {
      30             await Task.Delay(ms);
      31             throw new Exception(message);
      32         }
      33     }

       

      重要的補充與建議

      1.提高響應能力

      .NET有很多異步API我們都可以通過async/await構建調用提高響應能力,例如:

       1     class Program
       2     {
       3         static void Main(string[] args)
       4         {
       5             Demo();
       6             Console.ReadKey();
       7         }
       8         static async void Demo()
       9         {
      10             HttpClient httpClient = new HttpClient();
      11             var getTaskResult = await httpClient.GetStringAsync("http://www.rzrgm.cn/jonins/");
      12             Console.WriteLine(getTaskResult);
      13         }
      14     }

      這些API都有相同原則即以Async結尾。

       

      2.重要建議

      1.async方法需在其主體中具有await 關鍵字,否則它們將永不暫停。同時C# 編譯器將生成一個警告,此代碼將會以類似普通方法的方式進行編譯和運行。 請注意這會導致效率低下,因為由 C# 編譯器為異步方法生成的狀態機將不會完成任何任務。

      2.應將“Async”作為后綴添加到所編寫的每個異步方法名稱中。這是 .NET 中的慣例,以便更輕松區分同步和異步方法。

      3.async void 應僅用于事件處理程序。因為事件不具有返回類型(因此無法返回 Task 和 Task<T>)。 其他任何對 async void 的使用都不遵循 TAP 模型,且可能存在一定使用難度。

      例如:async void 方法中引發的異常無法在該方法外部被捕獲或十分難以測試 async void 方法。

      3.以非阻止方式處理等待任務

       

      異步編程準則

      異步編程的準則確定所需執行的操作是I/O-Bound還是 CPU-Bound。因為這會極大影響代碼性能,并可能導致某些構造的誤用。

      考慮兩個問題:

      1.你的代碼是否會“等待”某些內容,例如數據庫中的數據或web資源等?如果答案為“是”,則你的工作是 I/O-Bound

      2.你的代碼是否要執行開銷巨大的計算?如果答案為“是”,則你的工作是 CPU-Bound

      如果你的工作為 I/O-Bound,請使用 asyncawait(而不使用 Task.Run)。 不應使用任務并行庫。 
      如果你的工作為 CPU-Bound,并且你重視響應能力,請使用 asyncawait,并在另一個線程上使用 Task.Run 生成工作。 如果該工作同時適用于并發和并行,則應考慮使用任務并行庫。

       

      結語

      如果想要了解狀態機請戳:這里 。

       

      參考資料

      C#高級編程(第10版) C# 6 & .NET Core 1.0   Christian Nagel  

      果殼中的C# C#5.0權威指南  Joseph Albahari

      https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/index

      https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/async

      https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/await

      posted @ 2021-08-15 09:59  harrychinese  閱讀(201)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 午夜国产精品福利一二| 高清日韩一区二区三区视频| 久久综合色之久久综合| 国产成人精品无人区一区| 中文字幕日本六区小电影| 黑人猛精品一区二区三区| 来凤县| 久久精品激情亚洲一二区| 九九在线精品国产| 亚洲精品一区二区美女| 国产精品一区二区久久岳| 亚洲乱熟女一区二区三区| 国产亚洲无线码一区二区| 久久99热成人精品国产| 桃花岛亚洲成在人线AV| 国产午夜影视大全免费观看| 伊人久久大香线蕉AV网| 亚洲国产天堂久久综合226114| 伊人精品无码av一区二区三区| 亚洲精品一区二区区别| 国产老妇伦国产熟女老妇高清| 亚洲国产成人AⅤ片在线观看| 美女网站免费观看视频| 中文字幕日韩国产精品| 欧美做受视频播放| 99中文字幕精品国产| 亚洲日韩中文字幕在线播放| 国产午夜精品福利91| 来宾市| 老司机亚洲精品一区二区| 精品在线观看视频二区| 亚洲男人精品青春的天堂| 久久人人97超碰国产精品| 国产精品爽爽久久久久久| 无码日韩精品一区二区三区免费| A男人的天堂久久A毛片| 欧美最猛性xxxxx大叫| 国产av一区二区麻豆熟女| 99精品国产一区二区三区不卡| 国产漂亮白嫩美女在线观看| 亚洲AV永久无码嘿嘿嘿嘿|