C#多線程編程-必知必會

發現問題的能力,運用技術解決問題的能力,是一個技術人成長的關鍵

f9fef9634bf5b150909384b7bcf59cfb.jpeg

@圖片故事:洋姜的花,拍攝于2022年7月23日,地點:北京奧林匹克森林公園 ,攝影師:劉先生

概要:使用C#發起多線程任務十分簡單,本文旨在匯總多線程編程的注意事項,重點不在于如何發起多線程,主要內容如下:

  1. 控制線程并發數量

  2. 界定共享資源

  3. 加鎖并控制鎖范圍

  4. 子線程異常處理

  5. 未完成任務取消

    希望對小伙伴兒們有所幫助

01


控制線程并發數量

多線程以多任務并行的方式,加快業務處理速度,但如果線程數量超出了系統的承載能力,反倒會造成系統整體性能下降,如何合理地控制線程并發數量,是多線程開發的關鍵。

推薦采用信號量機制,可以在線程總數未知的情況下,有效地控制并發線程數量,并且以瀑布流的形式,連續執行后續線程,邏輯清晰可控,執行性能高效。

基礎代碼邏輯如下:

//semaphoreCount是設定的可并行運行的最大線程數量
//taskCount是需要發起的線程的數量
using (Semaphore semaphore = new Semaphore(semaphoreCount, semaphoreCount))
{var woker = new Worker();Task[] tasks = new Task[taskCount];for (int step = 0; step < taskCount; step++)   {        //獲取一個信號量,如果所有信號量都已使用,則等待直到一個被釋放        semaphore.WaitOne();        //獲得信號量之后,才能發起子線程        tasks[step] = Task.Factory.StartNew((data) => { woker.Work(data); }, innerData).ContinueWith((task) =>                                  {                                      //線程完成,釋放信號量                                      semaphore.Release();                                  });    }    //...
}

簡單來說,是由于分時操作系統,多任務之間存在線程上下文切換,有興趣的同學可以嘗試一下,一次性啟動2000個以上線程,查看計算機的資源耗用情況,以便有更真切的體會。

02


界定共享資源

線程共享資源,一類是業務本身需要多個子線程共同處理的資源,另一類是從性能角度考慮,需要被多個子線程共享的資源。

以數據查詢為例,數據庫連接是一種昂貴的資源,如果每個子線程單獨創建數據庫連接,必然會造成浪費,多個線程共用一個數據庫連接是更合理的選擇,因此,數據庫連接便是共享資源。

有興趣的同學可以測試一下,同時啟動50個以上線程,如果每個線程創建一個數據庫連接,會造成數據庫短時間內無法創建足夠連接而報錯。

03


加鎖并控制鎖范圍

對共享資源進行訪問時,需要加鎖保護,防止并發錯誤。

對于業務本身處理的共享資源,加鎖主要是防止數據處理錯誤;對于集合類型的共享資源,建議首選System.Collections.Concurrent?命名空間下的集合類型,以達到線程安全的目的;對于如數據庫連接之類的資源,加鎖是為了防止程序異常,如數據庫連接、HttpClient對象,在一個請求處理完之前,是不能被其他線程訪問的,因此需要加鎖,確保串行訪問是必須的。

對于鎖對象,推薦的寫法如下,至于是不是要加static?,要看具體業務場景,靜態變量的作用域是整個應用程序,如果有兩個以上請求同時到達,那么在訪問到加鎖代碼塊時,請求也是串行執行的,普通變量的作用域是當前對象,鎖范圍也是在當前對象內,請求間相互不影響。

readonly?object?locker?=?new?object();

04


子線程異常處理

概括成一句話是:在明確異常處理要做什么的情況下,才進行異常處理,否則,讓異常拋出,交由外層程序處理即可。參考我上一篇文章:異常處理,究竟是處理什么

多線程下異常處理的不同之處在于:子線程內的異常,不會直接拋出到主線程,而是保存在了Task對象的Exception屬性中。因此,需要開發小伙伴判斷線程狀態,進行異常處理。

基礎代碼邏輯如下:

Task.Factory.StartNew((data) => { woker.Work(data); }, innerData)            .ContinueWith((task) =>            {????????????????//判斷線程處理狀態,如果執行失敗,則拋出異常????????????????if (task.Status == TaskStatus.Faulted)????????????????{????????????????????throw?task.Exception;                }??????});

05


未完成任務取消

當某個子線程發生異常之后,取消后續相關線程的執行,符合絕大多數業務邏輯。

取消線程操作需要用到?CancellationTokenSource?類,線程啟動時,注冊“取消憑證(Token)”,當某個子線程發生異常后,調用CancellationTokenSource的Cancel()方法,通知相關線程取消操作。以后會寫一篇CancellationToken的詳細介紹。

基礎代碼邏輯如下:

//聲明 CancellationTokenSource
using (CancellationTokenSource cancellation = new CancellationTokenSource())
{Task[] tasks = new Task[taskCount];for (int step = 0; step < steps; step++){semaphore.WaitOne();//注冊cancellation.Tokentasks[step] = Task.Factory.StartNew((data) => { woker.Work(data); }, innerData, cancellation.Token).ContinueWith((task) =>{if (task.Status == TaskStatus.Faulted){//通知取消任務cancellation.Cancel(true);throw task.Exception;}semaphore.Release();});}
}

有多線程開發經歷的小伙伴,可以看一下自己的代碼,是否有對以上幾點的處理。以上內容均來自于我個人的經驗總結,如有疏漏,歡迎小伙伴補充指正。

最后,說一下對于多線程的認識,了解二次元的小伙伴應該知道一個詞:“結界”,線程與結界有很多相似之處,一個子線程就相當于一個結界,結界內外雖處于同一空間,但卻屬于不同的世界,結界阻斷了結界內外的聯系,但又可以相互作用,更多相似處,小伙伴們自己體會。


您的反饋是我堅持的動力,歡迎點贊,轉發,關注

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/282574.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282574.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282574.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

理解Go Interface

理解Go Interface1 概述Go語言中的接口很特別&#xff0c;而且提供了難以置信的一系列靈活性和抽象性。接口是一個自定義類型&#xff0c;它是一組方法的集合&#xff0c;要有方法為接口類型就被認為是該接口。從定義上來看&#xff0c;接口有兩個特點:接口本質是一種自定義類型…

『中級篇』Dockerfile詳解(17)

一般的&#xff0c;Dockerfile 分為四部分&#xff1a;基礎鏡像信息、維護者信息、鏡像操作指令和容器啟動時執行指令。 ####官網學習 https://docs.docker.com/engine/reference/builder/#usage ####FROM 必須為第一個命令&#xff0c;指定基礎鏡像 FROM <image> FROM &…

洛谷 1165日志分析

題目描述 M 海運公司最近要對旗下倉庫的貨物進出情況進行統計。目前他們所擁有的唯一記錄就是一個記錄集裝箱進出情況的日志。該日志記錄了兩類操作&#xff1a;第一類操作為集裝箱入庫操作&#xff0c;以及該次入庫的集裝箱重量&#xff1b;第二類操作為集裝箱的出庫操作。這些…

KestrelServer詳解[1]:注冊監聽終結點(Endpoint)

具有跨平臺能力的KestrelServer是最重要的服務器類型。針對KestrelServer的設置均體現在KestrelServerOptions配置選項上&#xff0c;注冊的終結點是它承載的最重要的配置選項。這里所謂的終結點&#xff08;Endpoint&#xff09;與“路由”介紹的終結點不是一回事&#xff0c;…

php截取字符串,帶中文,多余的省略號代替

function subtext($text, $length) {if(mb_strlen($text, utf8) > $length) {return mb_substr($text, 0, $length, utf8)....;} else {return $text;}}$str 我們是family happy family; echo subtext($str,5); //我們是fa...

數據庫添加

<body><form action"herozhuce.php" method"post"> <div>賬號<input type"text" name"account"/></div> <div>密碼<input type"text" name"password"/></div> &…

快來加入阿里云大學【云學院】班級助理招募—機會稍縱即逝,錯過遙遙無期!...

2019獨角獸企業重金招聘Python工程師標準>>> 如果你對云計算、大數據、云安全、人工智能領域感興趣~ 如果你想從事與此相關的工作~~ 如果你又喜歡邊交流邊學習的方式~ 那么&#xff0c;加入我們吧&#xff01; 我們將為你提供一個廣闊的平臺&#xff0c;讓你接觸到云…

深入理解ajax系列第五篇——進度事件

前面的話 一般地&#xff0c;使用readystatechange事件探測HTTP請求的完成。XHR2規范草案定義了進度事件Progress Events規范&#xff0c;XMLHttpRequest對象在請求的不同階段觸發不同類型的事件&#xff0c;所以它不再需要檢査readyState屬性。這個草案定義了與客戶端服務器通…

對象(poco)深度克隆

提供深度克隆對象功能,基于編譯表達式實現&#xff0c;性能與原生代碼幾無差別&#xff0c;遠超 json/binary 序列化實現。1. 簡單示例class Person {public int Id { get; set; }public string Name { get; set; }public int Age { get; set; }public DateTime Birth { get; s…

php將數字轉化為中文大寫人民幣格式

<?phpfunction cny($ns) {static $cnums array("零","壹","貳","叁","肆","伍","陸","柒","捌","玖"),$cnyunits array("圓","角","分&…

BZOJ1787 [Ahoi2008]Meet 緊急集合 LCA

歡迎訪問~原文出處——博客園-zhouzhendong 去博客園看該題解 題目傳送門 - BZOJ1787 題意概括 有一棵節點為n個(n≤500000)的樹。接下來m次詢問(m≤500000)&#xff0c;每次給出3個點 a,b,c &#xff0c;現在讓你求一個點 p &#xff0c;使得 dis(p,a) dis(p,b) dis(p,c) 最…

Linux之ACL權限控制

ACL權限控制主要目的是提供傳統的owner,group,other的read,wirte,execute權限之外的具體權限設置&#xff0c;可以針對單一用戶或組來設置特定的權限 設置ACL權限&#xff1a;setfacl查看ACL權限&#xff1a;getfacl 比如&#xff1a;某一目錄權限為 drwx------ 2 root root 40…

WIX、Squarespace、WordPress 三者的優劣分別是什么?

層出不窮的智能建站&#xff0c;模板建站&#xff0c;源碼建站&#xff0c;云建站&#xff0c;仿站&#xff0c;各種建站概念都拋灑于紅海之中。到底什么樣的網站適合自己&#xff0c;什么樣的網站值得我們去消費&#xff0c;什么樣的網站能長久&#xff0c;是個非常值得思考的…

平滑的加權輪詢均衡算法

前言在反向代理、路由、分布式應用調度等場景中通常都需要用到負載均衡算法&#xff0c;負載均衡的關鍵要點是“均衡”&#xff0c;即確保調用請求能均衡地落到多個處理節點上&#xff0c;負載均衡算法一般使用隨機或輪詢都可以保證均衡性。現實中由于服務器性能或資源分配的差…

php類精確驗證身份證號碼

<?php class check_IdCard {// $num為身份證號碼&#xff0c;$checkSex&#xff1a;1為男&#xff0c;2為女&#xff0c;不輸入為不驗證public function checkIdentity($num, $checkSex ) { // 不是15位或不是18位都是無效身份證號if (strlen($num) ! 15 && strl…

請說說接口和抽象類的區別?

1.從使用目的來看&#xff1a; 接口只是一個類間的協議&#xff0c;它并沒有規定怎么去實現&#xff1b; 抽象類可以重用你代碼使你的代碼更加簡潔&#xff1b;2.從行為來看&#xff1a; 接口可以多繼承,multi-implement 抽象類不能實例化&#xff0c;必須子類化才能實例化…

GitHub 使用

Git 是由 Linux 之父 Linus Tovalds 為了更好的管理 linux 內核開發而創立的分布是版本控制/軟件管理配置軟件. 簡單來說, Git 管理你的 代碼的歷史記錄 的工具. 首先注冊賬戶 (已經完成, moveofgod) 然后, 下載一個 GitHub Desktop(mac), msisgit 客戶端 (可以用命令行實現, …

LinkedHashMap 與 HashMap區別

2019獨角獸企業重金招聘Python工程師標準>>> LinkedHashMap 與 HashMap區別 &#xff08;非原創&#xff09; HashMap,LinkedHashMap,TreeMap都屬于Map Map 主要用于存儲鍵(key)值(value)對&#xff0c;根據鍵得到值&#xff0c;因此鍵不允許鍵重復,但允許值重復。 …

C# 11 中的 file local type

C# 11 中的 file local typeIntro在之前的版本中&#xff0c;我們想要一個類型只在當前的類型中生效&#xff0c;通常我們會在一個類的內部聲明一個 private 的類型以此來控制這個類型的訪問權限&#xff0c;在 C# 11 中引入了一個 file local type&#xff0c;僅在聲明類型的這…

PHP實現類似百度搜索自動完成(代碼簡單)

一、效果圖: 二、HTML代碼 <html lang"en"> <head><meta charset"utf-8"><title>jQuery UI 自動完成&#xff08;Autocomplete&#xff09; - 默認功能</title><link rel"stylesheet" href"/public/Auto…