日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

C#實現基于任務的異步編程模式_C#教程

作者:Ruby_Lu ? 更新時間: 2022-06-12 編程語言

一.延續任務

private async static void CallerWithAsync()
    {
      string result = await GreetingAsync("Stephanie");
      Console.WriteLine(result);
    }

    static Task<string> GreetingAsync(string name)
    {
      return Task.Run<string>(() =>
        {
            Thread.Sleep(10000);
            return name;
        });
    }
    
    GreetingAsync方法返回一個Task<string>對象。該Task<string>對象包含任務創建的信息,并保存到任務完成。Task類的ContinueWith方法定義了任務完成后就調用的代碼。
        private static void CallerWithContinuationTask()
        {

          var t1 = GreetingAsync("Stephanie");


          t1.ContinueWith(t =>
            {
              string result = t.Result;
              Console.WriteLine(result);
            });


        }

由于不使用await,線程不會在方法中等待,會執行完CallerWithContinuationTask()的代碼。不會再ContinueWith這里等待,所以需要一個前臺線程,不然會關閉所以線程。

二.同步上下文

CallerWithAsync和CallerWithContinuationTask方法在方法的不同階段使用了不同的線程。

static Task<string> GreetingAsync(string name)
        {
          return Task.Run<string>(() =>
            {
                Thread.Sleep(10000);
              Console.WriteLine("running greetingasync in thread {0} and task {1}", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
              return name;
            });
        }
        private async static void CallerWithAsync()
        {
          Console.WriteLine("started CallerWithAsync in thread {0} and task {1}", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
          string result = await GreetingAsync("Stephanie");
          Console.WriteLine(result);
          Console.WriteLine("finished GreetingAsync in thread {0} and task {1}", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
        }
        
        private static void CallerWithContinuationTask()
        {
          Console.WriteLine("started CallerWithContinuationTask in thread {0} and task {1}", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);

          var t1 = GreetingAsync("Stephanie");


          t1.ContinueWith(t =>
            {
              string result = t.Result;
              Console.WriteLine(result);
              Console.WriteLine("finished CallerWithContinuationTask in thread {0} and task {1}", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
            });
        }

使用async和await關鍵字,當await完成后,不需要進行任何處理,就能訪問UI線程。默認情況下,生成的代碼就會把線程轉換到擁有同步上下文的線程中。調用異步方法的線程分配給了同步上下文,await完成之后將繼續執行。
如果不使用相同的同步上下文,必須調用Task類的ConfigureAwait(false).例如,一個WPF應用程序,其await后面的代碼沒有任何用到UI元素的代碼。在這種情況下,避免切換到同步上下文會執行的更快。

string s1 = await GreetingAsync("Stephanie").ConfigureAwait(false);

三.使用多個異步方法

在一個異步方法里,可以調用一個或多個異步方法。如何編寫代碼,取決于一個異步方法的結果是否取決于另一個異步方法。

1.按順序調用異步方法

下面的例子,第第二個異步方法依賴于第一個異步方法的結果,await關鍵字就很有用。

  private async static void MultipleAsyncMethods()
  {
    string s1 = await GreetingAsync("Stephanie");
    string s2 = await GreetingAsync(s1);
    Console.WriteLine("Finished both methods.\n Result 1: {0}\n Result 2: {1}", s1, s2);
  }

2.使用組合器

如果第二個異步方法獨立于第一個,每個異步方法可以都不使用await,而是把每個異步方法的返回結果賦值給Task變量,就會運行的更快。

  private async static void MultipleAsyncMethodsWithCombinators1()
  {
    Task<string> t1 = GreetingAsync("Stephanie");
    Task<string> t2 = GreetingAsync("Matthias");
    await Task.WhenAll(t1, t2);
    Console.WriteLine("Finished both methods.\n Result 1: {0}\n Result 2: {1}", t1.Result, t2.Result);
  }
  • Task.WhenAll組合器可以接受多個同一類型的參數,并返回同一類型的值。
  • Task.WhenAll是在所有傳入方法的任務都完成了才返回Task。
  • WhenAny是在其中一個傳入方法的任務完成了就返回。

Task.WhenAll定義了幾個重載版本。如果所有的任務返回相同的類型,那么該類型的數組可用于await返回的結果。

string[] result = await Task.WhenAll(t1, t2);

四.轉換異步模式

https://www.jb51.net/article/244023.htm講了三種異步編程的模式。
并非所有的.NET Framework類在.NET 4.5中都引入了新的異步方法。還有許多類只提供類BeginXXX和EndXXX方法的異步模式,可以使用TaskFactory類的FromAsync方法,它可以把使用異步模式的方法轉換為基于任務的異步模式的方法。

/創建一個委托,并引用同步方法Greeting
        private static Func<string, string> greetingInvoker = Greeting;
        
        
        static IAsyncResult BeginGreeting(string name, AsyncCallback callback, object state)
        {
          return greetingInvoker.BeginInvoke(name, callback, state);
        }

        static string EndGreeting(IAsyncResult ar)
        {
          return greetingInvoker.EndInvoke(ar);
        }
        
        //FromAsync方法的前兩個參數是委托類型,傳入BeginGreeting, EndGreeting的地址。后兩個參數是輸入的參數和對象狀態參數。
        private static async void ConvertingAsyncPattern()
        {
          string r = await Task<string>.Factory.FromAsync<string>(BeginGreeting, EndGreeting, "Angela", null);
          Console.WriteLine(r);
        }

五.錯誤處理

如果調用異步方法沒有等待,將異步方法放在try/catch中,就不捕獲不到異常。

private static void DontHandle()
        {
          try
          {
            ThrowAfter(200, "first");
            // exception is not caught because this method is finished before the exception is thrown
          }
          catch (Exception ex)
          {
            Console.WriteLine(ex.Message);
          }
        }
        
        static async Task ThrowAfter(int ms, string message)
        {
            Console.Write("xxx");
          await Task.Delay(ms);
          throw new Exception(message);
        }

DontHandle方法調用ThrowAfter后,不會在該處等待,會繼續執行,不再保持對ThrowAfter方法的引用。
注意:返回void的異步方法永遠不會等待.異步方法最好返回一個Task類型。

1.異步方法的異常處理

使用await關鍵字,將其放在在try/catch中

private static async void HandleOneError()
        {
          try
          {
            await ThrowAfter(2000, "first");
          }
          catch (Exception ex)
          {
            Console.WriteLine("handled {0}", ex.Message);
          }
        }

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

如果按照下面的代碼,第二個異常將不會拋出。因為第一個異常已經拋出,直接調到catch塊內了。

private static async void StartTwoTasks()
        {
          try
          {
            await ThrowAfter(2000, "first");
            await ThrowAfter(1000, "second"); // the second call is not invoked because the first method throws an exception
          }
          catch (Exception ex)
          {
            Console.WriteLine("handled {0}", ex.Message);
          }
        }

使用Task.WhenAll,不管任務是否拋出異常,都會等到兩個任務完成。所以會拋出兩個異常。
但是,只能看見傳遞給Task.WhenAll方法的第一個任務的異常信息,雖然第二個異常會拋出,但不會顯示:

private async static void StartTwoTasksParallel()
            {
              Task t1 = null;
              Task t2 = null;
              try
              {
                t1 = ThrowAfter(2000, "first");
                 t2 = ThrowAfter(1000, "second");
                await Task.WhenAll(t1, t2);
              }
              catch (Exception ex)
              {
                // just display the exception information of the first task that is awaited within WhenAll
                Console.WriteLine("handled {0}", ex.Message);
              }
            }

3.使用AggregateException信息返回顯示異常

將Task.WhenAll返回的結果寫到一個Task變量中,catch語句只檢索到第一個任務的異常,但可以訪問外部任務taskResult的Exception屬性。Exception屬性是AggregateException類型。這個異常類型定義了InnerExceptions屬性,它包含了等待中的所有異常的列表。

private static async void ShowAggregatedException()
        {
          Task taskResult = null;
          try
          {
            Task t1 = ThrowAfter(2000, "first");
            Task t2 = ThrowAfter(1000, "second");
            await (taskResult = Task.WhenAll(t1, t2));
          }
          catch (Exception ex)
          {
            // just display the exception information of the first task that is awaited within WhenAll
            Console.WriteLine("handled {0}", ex.Message);
            foreach (var ex1 in taskResult.Exception.InnerExceptions)
            {
              Console.WriteLine("inner exception {0} from task {1}", ex1.Message, ex1.Source);
            }
          }
        }

六.取消異步方法

如果后臺任務可能運行很長時間,就可能用的取消任務。
取消框架基于協助行為,不是強制性的。一個運行時間很長的任務需要檢查自己是否被取消,在這種情況下,它的工作就是清理所有已打開的資源,并結束相關工作。
取消基于CancellationTokenSource類,該類可用于發送取消請求。請求發送給引用CancellationToken類的任務,其中CancellationToken類和CancellationTokenSource相關聯。

private CancellationTokenSource cts = new CancellationTokenSource();
        
        //添加一個按鈕,用于取消正在運行的任務。使用cts.Cancel();
         private void button5_Click(object sender, EventArgs e)
         {
             if (cts != null)
                 cts.Cancel();
         }
         
        private async void button4_Click(object sender, EventArgs e)
        {
            
            string s =  await AsyncTaskTest();
        }

        //向Task類的Run方法傳遞CancellationToken參數。但需要檢查是否請求了取消操作。
         Task<string> AsyncTaskTest()
        {
            return Task.Run(() =>
                {
                cts.Token.ThrowIfCancellationRequested();
                    Thread.Sleep(5000);
                    return "異步完成";
                }
            , cts.Token);
        }

原文鏈接:https://www.cnblogs.com/afei-24/p/6758058.html

欄目分類
最近更新