C# 線程知識--使用Task執行非同步操作

来源:https://www.cnblogs.com/liujianshe1990-/archive/2019/06/22/11068159.html
-Advertisement-
Play Games

Console.WriteLine("主線程執行其他處理"); 15: //主線程掛起1000毫秒,等待任務的完成。 16: Thread.Sleep(1000); 17: } 任務調度結果: 2.等待任務的完成並獲取返回值 使用任務執行非同步操作時,最主要的是要後的任務完成時的返回值。在任務類中有一 ...


            Console.WriteLine("主線程執行其他處理");
  15:              //主線程掛起1000毫秒,等待任務的完成。
  16:              Thread.Sleep(1000);
  17:          }

任務調度結果:image

2.等待任務的完成並獲取返回值

     使用任務執行非同步操作時,最主要的是要後的任務完成時的返回值。在任務類中有一個實例方法Wait(有許多重載版本)他能等待任務的完成,我們也可以通過Task類的派生類Task<TResult>創建一個非同步任務,並指定任務完成時返回值的類型,這樣可以通過Task<TResult>的實例對象獲取到任務完成後的返回值。創建一個非同步任務並執行0到100求和操作返回最後的計算結果,示例代碼:

   1:  static void TaskWait() {
   2:              //創建任務
   3:              Task<int> task = new Task<int>(() =>
   4:              {
   5:                  int sum = 0;
   6:                  Console.WriteLine("使用Task執行非同步操作.");
   7:                  for (int i = 0; i < 100; i++)
   8:                  {
   9:                      sum+=i;
  10:                  }
  11:                  return sum;
  12:              });
  13:              //啟動任務,並安排到當前任務隊列線程中執行任務(System.Threading.Tasks.TaskScheduler)
  14:              task.Start();
  15:   
  16:              Console.WriteLine("主線程執行其他處理");
  17:              //等待任務的完成執行過程。
  18:              task.Wait();
  19:              //獲得任務的執行結果
  20:              Console.WriteLine("任務執行結果:{0}", task.Result.ToString());
  21:  }

執行結果:image

Task類還有一些靜態方法,WaitAll用於等待提供的所有 System.Threading.Tasks.Task 對象完成執行過程和Wait用於等待提供的任一個 System.Threading.Tasks.Task 對象完成執行過程,這兩個方法都有一些重載版本。

//等待所有任務完成  
public static void WaitAll(params Task[] tasks);
//等待任意一個任務完成
public static int WaitAny(params Task[] tasks);

3.使用ContinueWith方法在任務完成時啟動一個新任務

     在使用能夠Task類的Wait方法等待一個任務時或派生類的Result屬性獲得任務執行結果都有可能阻塞線程,為瞭解決這個問題可以使用ContinueWith方法,他能在一個任務完成時自動啟動一個新的任務來處理執行結果。

示例代碼:

   1:  static void TaskContinueWith()
   2:          {
   3:              //創建一個任務
   4:              Task<int> task = new Task<int>(() =>
   5:              {
   6:                  int sum = 0;
   7:                  Console.WriteLine("使用Task執行非同步操作.");
   8:                  for (int i = 0; i < 100; i++)
   9:                  {
  10:                      sum += i;
  11:                  }
  12:                  return sum;
  13:              });
  14:              //啟動任務,並安排到當前任務隊列線程中執行任務(System.Threading.Tasks.TaskScheduler)
  15:              task.Start();
  16:              Console.WriteLine("主線程執行其他處理");
  17:              //任務完成時執行處理。
  18:              Task cwt = task.ContinueWith(t => { 
  19:                  Console.WriteLine("任務完成後的執行結果:{0}", t.Result.ToString()); 
  20:              });
  21:              Thread.Sleep(1000);
  22:          }

執行結果:image

上述示例中任務不是等待完成來顯示執行結果,而是使用ContinueWith方法,它能夠知道任務在什麼時候完成並啟動一個新的任務來執行任務完成後的處理。ContinueWith方法具有一些重載版本,這些重載版本允許指定延續任務需要使用的數據、延續任務的工作方式(System.Threading.Tasks.TaskContinuationOptions的枚舉值按位OR運行的結果)等。

4.創建父子任務和任務工廠的使用

    通過Task類創建的任務是頂級任務,可以通過使用 TaskCreationOptions.AttachedToParent 標識把這些任務與創建他的任務相關聯,所有子任務全部完成以後父任務才會結束操作。示例如下:

   1:  static void ParentChildTask() {
   2:              Task<string[]> parent = new Task<string[]>(state => {
   3:                  Console.WriteLine(state);
   4:                  string[] result=new string[2];
   5:                  //創建並啟動子任務
   6:                  new Task(() => { result[0]= "我是子任務1。"; },TaskCreationOptions.AttachedToParent).Start();
   7:                  new Task(() => { result[1] = "我是子任務2。"; }, TaskCreationOptions.AttachedToParent).Start();
   8:                  return result;
   9:              },"我是父任務,併在我的處理過程中創建多個子任務,所有子任務完成以後我才會結束執行。");
  10:              //任務處理完成後執行的操作
  11:              parent.ContinueWith(t => {
  12:                  Array.ForEach(t.Result, r=>Console.WriteLine(r));
  13:              });
  14:              //啟動父任務
  15:              parent.Start();
  16:              Console.Read();
  17:          }

執行結果:image

    如果需要創建一組具有相同狀態的任務時,可以使用TaskFactory類或TaskFactory<TResult>類。這兩個類創建一組任務時可以指定任務的CancellationToken、TaskCreationOptions、TaskContinuationOptions和TaskScheduler預設值。示例代碼:

   1:          static void TaskFactoryApply()
   2:          {
   3:              Task parent = new Task(() =>
   4:              {
   5:                  CancellationTokenSource cts = new CancellationTokenSource(5000);
   6:                  //創建任務工廠
   7:                  TaskFactory tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
   8:                  //添加一組具有相同狀態的子任務
   9:                  Task[] task = new Task[]{
  10:                      tf.StartNew(() => { Console.WriteLine("我是任務工廠里的第一個任務。"); }),
  11:                      tf.StartNew(() => { Console.WriteLine("我是任務工廠里的第二個任務。"); }),
  12:                      tf.StartNew(() => { Console.WriteLine("我是任務工廠里的第三個任務。"); })
  13:                  };
  14:              });
  15:              parent.Start();
  16:              Console.Read();
  17:          }

執行結果:image

5.任務內部實現和任務調度

    任務內部有一組構成任務狀態的屬性,標識任務的唯一Id、表示任務的執行狀態(TaskStatus)、任務創建時提供的回調函數的引用和傳遞給回調函數的數據對象AsyncState、對任務創建時的任務調度對象(TaskScheduler)的引用、對父任務的引用以及對執行上下文的引用和ManualResetEventSlim對象的引用。Task類和Task<TResult>類都實現了標準的釋放資源的介面,允許在任務完成處理的時候使用Dispose方法釋放資源(關閉ManualResetEventSlim對象實例)。可以使用Task類的CurrentId屬性獲得正在執行的任務的Id,如果沒有任務在執行CurrentId返回值為null,CurrentId是一個int?可空類型的屬性。任務執行的生命周期通過TaskStatus類型的一個值來表示,TaskStatus所包含的值:

public enum TaskStatus
        {
            Created = 0,
            WaitingForActivation = 1,
            WaitingToRun = 2,
            Running = 3,
            WaitingForChildrenToComplete = 4,
            RanToCompletion = 5,
            Canceled = 6,
            Faulted = 7,
        }

      我們可以通過Task類的Exception屬性獲得任務在執行過程中的所有異常

,Exception是一個AggregateException類型的屬性。Task類提供了IsCanceled、IsCompleted、IsFaulted屬性來獲得任務的完成狀態。通過ContinueWith、ContinueWhenAll、ContinueWhenAny和FromAsync創建的後續任務都處於WaitingForActivation 狀態,這個狀態的任務會在父任務完成後自動執行。

      在任務內部由TaskScheduler類調度任務的執行,該類是一個抽象類,FCL中從他派生了兩個派生類:ThreadPoolTaskScheduler線程池任務調度器和SynchronizationContextTaskScheduler同步上下文任務調度器。所有任務預設都是採用ThreadPoolTaskScheduler調度任務,他是採用線程池來執行任務,可以通過TaskScheduler類的靜態屬性Default獲得對預設任務調度器的引用。SynchronizationContextTaskScheduler任務調度器能夠用在Window form、WPF等應用程式,他的任務調度是採用的GUI線程,所以他能同步更新UI組件,可以通過TaskScheduler類的靜態方法FromCurrentSynchronizationContext獲得對一個同步上下文任務調度起的引用。

任務調度示例:

   1:    private void button1_Click(object sender, EventArgs e)
   2:          {
   3:               //獲得同步上下文任務調度器
   4:             TaskScheduler m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
   5:   
   6:              //創建任務,並採用預設任務調度器(線程池任務調度器)執行任務
   7:              Task<int> task = new Task<int>(() =>
   8:              {
   9:                  //執行複雜的計算任務。
  10:                  Thread.Sleep(2000);
  11:                  int sum = 0;
  12:                  for (int i = 0; i < 100; i++)
  13:                  {
  14:                      sum += i;
  15:                  }
  16:                  return sum;
  17:              });
  18:               var cts=new CancellationTokenSource();
  19:              //任務完成時啟動一個後續任務,並採用同步上下文任務調度器調度任務更新UI組件。
  20:              task.ContinueWith(t => {this.label1.Text="採用SynchronizationContextTaskScheduler任務調度器更新UI。\r\n計算結果是:"+task.Result.ToString(); },
  21:                 cts.Token ,TaskContinuationOptions.AttachedToParent,m_syncContextTaskScheduler);
  22:              task.Start();
  23:          }

執行結果:image

    本文簡單的介紹了使用Task類來執行非同步操作以及任務的內部實現與任務調度。在執行複雜非同步操作時,可以採用任務來執行,他能更好的知道非同步操作在何時完成以及返回非同步操作的執行結果。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、插入排序的介紹 插入排序的工作方式非常像人們排序一手撲克牌一樣。開始時,我們的左手為空並且桌子上的牌面朝下。然後,我們每次從桌子上拿走一張牌並將它插入左手中正確的位置。為了找到一張牌的正確位置,我們從右到左將它與已在手中的每張牌進行比較,如下圖所示: 那插曲排序是如何藉助上面提到的思想來實現排序 ...
  • 看了劉江老師教程這麼多天,卧槽,我才發現他也曾躋身於行伍之間,interesting 劉老師這波講解很到位,告訴你如何編寫單例視圖的時候忽然告訴你,其實不用這麼麻煩,我們有通用視圖,那些總是要做相似的行為的視圖,咱們就寫一個好了,解放生產力不就是進步嗎? 好的廢話不說進入正題,先修改一波detail ...
  • 在C#的List集合中,如果要查找List集合是否包含某一個值或者對象,如果不使用List集合類的擴展方法的話一般會使用for迴圈或者foreach遍歷來查找,其實List集合類中的擴展方法Contain方法即可實現此功能,Contain方法的簽名為bool Contains(T item),ite ...
  • 在C#中的List集合操作過程中,有時候需要清空List集合中的元素對象,將之重置為一個初始化的List集合對象,此時就可以使用到List集合的擴展方法Clear()方法,此方法將清空List集合中所有的元素對象,清空後List集合中的元素個數為0。 例如有個List<int>的集合list1,內部 ...
  • 前言: 項目實戰中不論是業務編碼還是通用編碼,總會歸納出一些通用的工具類。放入項目中一勞永逸,讓兄弟姐妹們避免編寫重覆代碼。所以利用了工作之餘的時間,將這些散落在多個項目中精緻優雅的工具類,歸納起來形成工程,方便後續工作的使用和便捷開發。 根據實際需求,編寫了此工具。目前只支持SQLServer數據 ...
  • 開篇:上一篇我們瞭解了一個ASP.Net頁面請求的核心處理入口,它經歷了三個重要的入口,分別是:ISAPIRuntime.ProcessRequest()、HttpRuntime.ProcessRequest()以及HttpApplication.Init()。其中,在HttpApplication ...
  • 在IT行業中,通常被人稱為:碼農,程式猿。在日常開發中,我們不能滿足於代碼的搬運,不能只會百度搜索,Copy和Paste。 ...
  • 開始以前,先認識一下WinForm控制項數據綁定的兩種形式,簡單數據綁定和複雜數據綁定。 1. 簡單的數據綁定 例1 簡單的數據綁定是將用戶控制項的某一個屬性綁定至某一個類型實例上的某一屬性。 採用如下形式進行綁定:引用控制項.DataBindings.Add("控制項屬性", 實例對象, "屬性名", t ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...