C#併發編程-1 併發編程概述

来源:https://www.cnblogs.com/wwwen/archive/2022/07/28/16528465.html
-Advertisement-
Play Games

一 併發編程簡介 1.1 關於併發和並行 併發和並行的概念: 併發:(Concurrent),在某個時間段內,如果有多個任務執行,即有多個線程在操作時,如果系統只有一個CPU,則不能真正同時進行一個以上的線程, 它只能把CPU運行時間劃分成若幹個時間段,再將時間段分配給各個線程執行,在一個時間段的線 ...


一 併發編程簡介

1.1 關於併發和並行

併發和並行的概念:

  • 併發:(Concurrent),在某個時間段內,如果有多個任務執行,即有多個線程在操作時,如果系統只有一個CPU,則不能真正同時進行一個以上的線程,

                它只能把CPU運行時間劃分成若幹個時間段,再將時間段分配給各個線程執行,在一個時間段的線程代碼運行時,其它線程處於掛起狀態。

  • 並行:(Parallel),當系統有一個以上CPU時,則線程的操作有可能非併發。

                當一個CPU執行一個線程時,另一個CPU可以執行另一個線程,兩個線程互不搶占CPU資源,可以同時進行。

併發和並行是即相似又有區別的兩個概念:

  • 併發是指兩個或多個事件在同一時間間隔內發生;
  • 並行是指兩個或者多個事件在同一時刻發生。

1.2 關於併發編程和並行編程

併發編程的形式:

  • 多線程編程:使用多個線程執行程式;
  • 響應式編程。
  • 非同步編程:APM,EAP,TAP。推薦使用 async await關鍵字的TAP ( Task-based Asynchronous Pattern );

並行編程:把正在執行的大量任務分割成小塊,分配個多個同時運行的線程,讓它們在不同的核上獨立運行。讓處理器的利用效率最大化。

 

二 非同步編程簡介

2.1 async await關鍵字

現代的非同步.Net程式使用兩個關鍵字,async和await。async關鍵字加在方法聲明上,它的主要目的是使方法內的await關鍵字生效。

如果async方法有返回值,應返回Task<T>,如果沒有返回值,應返回Task。Task類型相當於future,用來在非同步方法結束時通知調用程式。

一個非同步方法例子:

async Task MethodAsync() 
{
    var i = 10;
    await Task.Delay(1000);
    i++;
    await Task.Delay(1000);
    Trace.WriteLine(i);
}

async方法在開始時以同步方式執行。在async方法內部,await關鍵字對它的參數執行一個非同步等待。

它首先檢測操作是否已經完成,如果完成了,就繼續以同步方式運行,否則會暫停方法,並返回,留下一個未完成的Task。

一段時間後,操作完成,async方法就恢復,從await出繼續往下執行。

 2.2 ConfigureAwait ( )

一個 async 方法是由多個同步執行的程式塊組成的,每個同步程式塊之間由 await 語句分隔。

第一個同步程式塊在調用這個方法的線程中運行,那await之後的同步程式塊在哪個線程運行呢?

一個常見的情況是,用 await 語句等待一個任務完成,當該方法在 await 處暫停時,就可以捕捉上下文(context)。

如果當前 SynchronizationContext 不為空,這個上下文就是當前SynchronizationContext。如果當前 SynchronizationContext 為空,則這個上下文為當前TaskScheduler。

該方法會在這個上下文中繼續運行。一般來說,運行 UI 線程時採用 UI 上下文,處理 ASP.NET 請求時採用 ASP.NET 請求上下文,其他很多情況下則採用線程池上下文。

因此,在上面的代碼中,每個同步程式塊會試圖在原始的上下文中恢復運行。如果在 UI線程中調用 MethodAsync,這個方法的每個同步程式塊都將在此 UI 線程上運行。但

是,如果線上程池線程中調用,每個同步程式塊將線上程池線程上運行。

要控制這種行為,可以在 await 中使用 ConfigureAwait 方法,將參數 continueOnCapturedContext設為 false。接下來的代碼剛開始會在調用的線程里運行,在被 await 暫停後,

則會線上程池線程里繼續運行。

async Task MethodAsync() 
{
    //在調用線程運行
    var i = 10;
    await Task.Delay(1000).ConfigureAwait(false);
    //線上程池線程運行
    i++;
    await Task.Delay(1000);
    Trace.WriteLine(i);
}

 2.3 異常處理

使用async await時,自然要處理異常。一旦非同步方法拋出異常,該異常會放在返回的 Task 對象中,並且這個 Task對象的狀態變為“已完成”。

當 await 調用該 Task 對象時,await 會獲得並(重新)拋出該異常,並且保留著原始的棧軌跡。

async Task MethodAsync() 
{
    //發生異常時,任務結束,不會直接拋出異常。
    var task = ThrowExceptionAsync();
    try
    {
        //await時,拋出異常
        await task;
    }
    catch (Exception ex)
    {
        Trace.WriteLine(ex.StackTrace);
    }
}

async Task ThrowExceptionAsync() 
{
    await Task.Delay(1000);
    throw new Exception("ThrowExceptionAsync");
}

2.4 一旦在代碼中使用了非同步,最好一直使用

關於非同步方法,還有一條重要的準則:一旦在代碼中使用了非同步,最好一直使用。調用非同步方法時,應該(在調用結束時)用 await 等待它返回的 task 對象。

一定要避免使用Task.Wait 或 Task<T>.Result 方法,因為它們會導致死鎖。參考一下下麵這個方法:

void Deadlock()
{
    // 開始延遲
    Task task = WaitAsync();
    // 同步程式塊,正在等待非同步方法完成
    task.Wait();
}
async Task WaitAsync()
{
    // 這裡 awati 會捕獲當前上下文
    await Task.Delay(TimeSpan.FromSeconds(1));
    // 這裡會試圖用上面捕獲的上下文繼續執行
}

如果從 UI 或 ASP.NET 的上下文調用這段代碼,就會發生死鎖。這是因為,這兩種上下文每次只能運行一個線程。Deadlock 方法調用 WaitAsync 方法,WaitAsync 方法開始調用delay 語句。

然後,Deadlock 方法(同步)等待 WaitAsync 方法完成,同時阻塞了上下文線程。當 delay 語句結束時,await 試圖在已捕獲的上下文中繼續運行 WaitAsync 方法,但這個步驟無法成功,

因為上下文中已經有了一個阻塞的線程,並且這種上下文只允許同時運行一個線程。

這裡有兩個方法可以避免死鎖:

在 WaitAsync 中使用 ConfigureAwait(false)(導致 await 忽略該方法的上下文),

或者用 await 語句調用 WaitAsync 方法(讓 Deadlock變成一個非同步方法)。

 

三 並行編程簡介

如果程式中有大量的計算任務,並且這些任務能分割成幾個互相獨立的任務塊,可以考慮使用並行編程。並行編程可臨時提高 CPU 利用率,以提高吞吐量。

並行的形式有兩種:

  • 數據並行(data parallelism):有大量的數據需要處理,並且每一塊數據的處理過程基本上是彼此獨立的。

  • 任務並行(task parallelim):需要執行大量任務,並且每個任務的執行過程基本上是彼此獨立的。任務並行可以是動態的,如果一個任務的執行結果會產生額外的任務,這些新增的任務也可以加入任務池。

3.1 數據並行

實現數據並行有幾種不同的做法。

一種做法是使用 Parallel.ForEach 方法,它類似於foreach 迴圈,應儘可能使用這種做法。

Parallel 類也提供 Parallel.For 方法,這類似於 for 迴圈,當數據處理過程基於一個索引時,可使用這個方法。

另一種做法是使用 PLINQ(Parallel LINQ), 它為 LINQ 查詢提供了 AsParallel 擴展。

與PLINQ 相比,Parallel 對資源更加友好,Parallel 與系統中的其他進程配合得比較好 , 而PLINQ 會試圖讓所有的 CPU 來執行本進程。

//Parallel.ForEach
void RotateMatrices(IEnumerable<Matrix> matrices, float degrees)
{
    Parallel.ForEach(matrices, matrix => matrix.Rotate(degrees));
}

//PLINQ
IEnumerable<bool> PrimalityTest(IEnumerable<int> values)
{
    return values.AsParallel().Select(val => IsPrime(val));
}

不管選用哪種方法,在並行處理時有一個非常重要的準則。只要任務塊是互相獨立的,並行性就能做到最大化。

一旦在多個線程中共用狀態,就必須以同步方式訪問這些狀態,那樣程式的並行性就變差了。

有多種方式可以控制並行處理的輸出。可以把結果存在某些併發集合,或者對結果進行聚合。

3.2 任務並行

任務並行關註執行任務,Parallel 類的 Parallel.Invoke 方法可以以 分叉 / 聯合”(fork/join)的方式的使任務並行。

調用該方法時,把要並行執行的委托(delegate)作為傳入參數:

void ProcessArray(double[] array)
{
    Parallel.Invoke(
    () => ProcessPartialArray(array, 0, array.Length / 2),
    () => ProcessPartialArray(array, array.Length / 2, array.Length)
    );
}
void ProcessPartialArray(double[] array, int begin, int end)
{
    // CPU 密集型的操作……
}

任務並行也強調任務塊的獨立性。委托(delegate)的獨立性越強,程式的執行效率就越高。

 

 四 併發編程的集合

併發編程所用到的集合有兩類:

  • 併發集合;
  • 不可變集合。

4.1 併發集合

多個線程可以用安全的方式同時更新併發集合。大多數併發集合使用快照snapshot),

當一個線程在增加或刪除數據時,另一個線程也能枚舉數據。比起給常規集合加鎖以保護數據的方式,採用併發集合的方式要高效得多。

4.2 不可變集合

不可變集合實際上是無法修改的。要修改一個不可變集合,需要建立一個新的集合來代表這個被修改了的集合。


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

-Advertisement-
Play Games
更多相關文章
  • 前言 😋 嗨嘍,大家好呀~這裡是愛看美女的茜茜吶 本次採集網介紹:圖書頻道-全球最大中文網上書店 專業提供小說傳記,青春文學,成功勵志,投資理財等各品類圖書 暢銷榜最新報價、促銷、評論信息,引領最新網上購書體驗! 環境使用 🎈: Python 3.8 Pycharm 模塊使用 🎠: reque ...
  • 1. 簡介 1.1什麼是Mybatis MyBatis 是一款優秀的持久層框架 它支持自定義 SQL、存儲過程以及高級映射。 MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數和獲取結果集的工作。 MyBatis 可以通過簡單的 XML 或註解來配置和映射原始類型、介面和 Java POJ ...
  • 問題描述 用python 讀取csv文件時,報錯utf-8' codec can't decode byte 0xff in position 0: invalid start byte 問題原因 打開所用的編碼方式不對,需要指定該csv文件所用編碼 解決方法 1.找到該csv文件所用編碼方法 用記 ...
  • 兄弟們,溫故而知新,可以為師矣。 就是說,我們所學過的東西,要去多複習,這樣才能總結出屬於自己的理解,這樣就可以做老師了。 但是我以為的我以為,後面可以改成,將自己所學及所領會的教給別人,這樣才能更加記憶深刻。 今日內容:Python將多個文件多列進行關聯 知識點 文件讀寫 基礎語法 異常處理 迴圈 ...
  • 背景 過去,我們運維著“能做一切”的大型單體應用程式。這是一種將產品推向市場的很好的方式,因為剛開始我們也只需要讓我們的第一個應用上線。 而且我們總是可以回頭再來改進它的。部署一個大應用總是比構建和部署多個小塊要容易。 集中式: 集群: 分散式: 分散式和集中式會配合使用。 我們在搭建網站的時候,為 ...
  • static關鍵字 1.Java中的靜態 1.1static修飾成員變數 static修飾的成員變數屬於類、也稱為類變數,類對象可以使用。使用時可以直接用類名調用。 定義格式:`static 數據類型 變數名;` 例子: class A{ static String city="China"; } ...
  • 1.此為GitHub項目的學習記錄,記錄著我的思考,代碼基本都有註釋。 2.可以作為Python初學者鞏固基礎的絕佳練習,原題有些不妥的地方我也做了一些修正。 3.建議大家進行Python編程時使用英語,工作時基本用英語。 4.6~17題為level1難度,18-22題為level3難度,其餘都為l ...
  • [數據結構-線性表1.1] 數組(.NET源碼學習) 數組,一種數據類型(在絕大數語言中不是基本數據類型)且為引用類型,在記憶體中以連續的記憶體單元進行分配,所以其大小在創建對象後為定值,不可更改。 一.記憶體分配 對於兩種不同數據類型而言,其記憶體分配方式是不同的。值類型直接在棧(C#中稱為堆棧Stack ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...