如何減少鎖競爭并細化鎖粒度以提高 Rust 多線程程序的性能?

在并發編程中,鎖(Lock)是一種常用的同步機制,用于保護共享數據免受多個線程同時訪問造成的競態條件(Race Condition)。然而,不合理的鎖使用會導致嚴重的性能瓶頸,特別是在高并發場景下。本文將探討如何通過減少鎖競爭細化鎖粒度來提升 Rust 多線程程序的性能。


一、什么是鎖競爭?

鎖競爭(Lock Contention)指的是多個線程嘗試同時獲取同一個鎖時發生的沖突。當一個線程持有鎖時,其他線程必須等待該鎖釋放,這會導致線程阻塞或自旋,從而降低程序吞吐量和響應速度。

示例:粗粒度鎖導致的性能問題

use std::sync::{Arc, Mutex};
use std::thread;fn main() {let data = Arc::new(Mutex::new(vec![0; 10000]));let mut handles = vec![];for _ in 0..4 {let data = Arc::clone(&data);let handle = thread::spawn(move || {for i in 0..10000 {let mut d = data.lock().unwrap();d[i % 10000] += 1;}});handles.push(handle);}for handle in handles {handle.join().unwrap();}println!("{:?}", data.lock().unwrap());
}

在這個例子中,我們使用了一個全局的 Mutex 來保護整個數組。雖然保證了安全性,但由于所有線程都爭搶同一把鎖,造成了嚴重的鎖競爭,性能會顯著下降。


二、鎖粒度的概念與優化思路

鎖粒度(Lock Granularity)是指每次加鎖所保護的數據范圍大小。鎖粒度越細,意味著每個鎖保護的數據越少,從而減少不同線程之間的沖突,提高并發效率。

粗粒度 vs 細粒度鎖對比:

類型描述優點缺點
粗粒度鎖一把鎖保護大量共享資源實現簡單高競爭,低并發性
細粒度鎖每個子資源都有獨立鎖減少競爭,提高并發實現復雜,內存開銷大

三、Rust 中的鎖類型與選擇建議

在 Rust 中,常見的鎖包括:

  • std::sync::Mutex<T>:標準庫提供的互斥鎖。
  • parking_lot::Mutex<T>:第三方庫 parking_lot 提供的更高效的互斥鎖,適用于大多數高性能場景。
  • RwLock<T>:讀寫鎖,允許多個讀操作同時進行。

推薦優先使用 parking_lot::Mutex,其性能通常優于標準庫的 Mutex,并且支持遞歸鎖等高級特性。


四、如何細化鎖粒度?

方法一:對數據結構分片(Sharding)

對于大型共享數據結構(如哈希表、數組),可以將其拆分成多個部分,每個部分由獨立的鎖保護。

示例:將數組分片為多個 Mutex
use std::sync::{Arc, Mutex};
use std::thread;fn main() {const NUM_SHARDS: usize = 16;let data: Vec<_> = (0..NUM_SHARDS).map(|_| Arc::new(Mutex::new(0))).collect();let mut handles = vec![];for _ in 0..4 {let data = data.clone();let handle = thread::spawn(move || {for _ in 0..10000 {let index = rand::random::<usize>() % NUM_SHARDS;let mut val = data[index].lock().unwrap();*val += 1;}});handles.push(handle);}for handle in handles {handle.join().unwrap();}for (i, shard) in data.iter().enumerate() {println!("Shard {}: {}", i, *shard.lock().unwrap());}
}

在這個例子中,我們將共享計數器分成了 16 個片段,每個片段都有自己的鎖。這樣大大減少了鎖競爭的概率。


方法二:使用讀寫鎖(RwLock)

如果你的數據結構有“讀多寫少”的特點,可以考慮使用 RwLock,它允許多個讀線程同時訪問,但只允許一個寫線程獨占。

示例:使用 RwLock 提高讀取并發
use std::sync::{Arc, RwLock};
use std::thread;fn main() {let data = Arc::new(RwLock::new(vec![0; 1000]));for _ in 0..4 {let data = Arc::clone(&data);thread::spawn(move || {for _ in 0..100 {let read = data.read().unwrap();// 只讀操作assert!(read.len() == 1000);}});}// 寫操作較少let mut write = data.write().unwrap();write[0] = 1;
}

方法三:避免不必要的鎖 —— 使用無鎖數據結構或原子變量

在某些情況下,我們可以完全避免使用鎖,改用無鎖(Lock-Free)算法或原子操作(Atomic Types)。

例如,使用 AtomicUsize 替代簡單的計數器:

use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;fn main() {let counter = Arc::new(AtomicUsize::new(0));let mut handles = vec![];for _ in 0..4 {let counter = Arc::clone(&counter);let handle = thread::spawn(move || {for _ in 0..10000 {counter.fetch_add(1, Ordering::Relaxed);}});handles.push(handle);}for handle in handles {handle.join().unwrap();}println!("Counter value: {}", counter.load(Ordering::Relaxed));
}

這種方法不僅避免了鎖競爭,還提高了執行效率。


五、進階技巧:使用 Rayon 并行迭代器簡化并發邏輯

如果你的程序是 CPU 密集型任務,且不需要頻繁訪問共享狀態,可以考慮使用 Rayon,這是一個 Rust 的并行迭代器庫,能夠自動將迭代操作并行化,而無需手動管理鎖。

示例:使用 Rayon 進行并行求和
use rayon::prelude::*;fn main() {let v: Vec<i32> = (0..100000).collect();let sum: i32 = v.par_iter().sum();println!("Sum: {}", sum);
}

Rayon 內部使用工作竊取(Work Stealing)調度算法,能高效地利用多核 CPU 資源。


六、總結

優化多線程程序的關鍵在于:

  • 減少鎖競爭:盡量避免多個線程頻繁爭搶同一把鎖。
  • 細化鎖粒度:將共享資源劃分為多個小塊,各自加鎖。
  • 合理選擇鎖類型:根據讀寫模式選擇合適的鎖(Mutex / RwLock)。
  • 盡可能避免鎖:使用原子變量、無鎖結構或并行庫(如 Rayon)。

在 Rust 中,得益于其強大的類型系統和所有權模型,我們可以安全地編寫高性能的并發代碼。希望這篇文章能幫助你在開發多線程程序時做出更好的設計決策!


參考資料

  • Rust 標準庫文檔 - Mutex
  • parking_lot 文檔
  • Rust 并發指南
  • Rayon 官方文檔

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

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

相關文章

AGV智能搬運機器人:富唯智能引領工業物流高效變革

在智能制造與工業4.0深度融合的今天&#xff0c;物流環節的高效與精準已成為企業核心競爭力的關鍵。富唯智能憑借其自主研發的AGV智能搬運機器人&#xff0c;以創新技術重塑工業物流標準&#xff0c;助力企業實現降本增效的跨越式發展。 一、技術突破&#xff1a;精準導航與智能…

K8s 資源分類

K8s 資源分類圖譜 內置資源的分類 1、工作負載相關&#xff1a; Pod&#xff1a;最小的部署單元&#xff0c;包含一個或多個容器。 Deployment&#xff1a;管理無狀態應用的副本和滾動更新。 StatefulSet&#xff1a;適用于有狀態應用&#xff08;如數據庫&#xff09;&#…

VLM-AD:通過視覺語言模型監督實現端到端自動駕駛

《VLM-AD: End-to-End Autonomous Driving through Vision-Language Model Supervision》2024年12月發表&#xff0c;來自Cruise和美國東北大學的論文。 人類駕駛員依靠常識推理來駕馭多樣化和動態的現實世界場景。現有的端到端&#xff08;E2E&#xff09;自動駕駛&#xff0…

目標檢測中的損失函數(三) | SIoU WIoUv1 WIoUv2 WIoUv3

&#x1f680;該系列將會持續整理和更新BBR相關的問題&#xff0c;如有錯誤和不足懇請大家指正&#xff0c;歡迎討論&#xff01;&#xff01;&#xff01; SCYLLA-IoU&#xff08;SIoU&#xff09;來自掛在2022年arxiv上的文章&#xff1a;《SIoU Loss: More Powerful Learnin…

http Status 400 - Bbad request 網站網頁經常報 HTTP 400 錯誤,清緩存后就好了的原因

目錄 一、HTTP 400 錯誤的常見成因(一)問題 URL(二)緩存與 Cookie 異常(三)請求頭信息錯誤(四)請求體數據格式不正確(五)文件尺寸超標(六)請求方法不當二、清緩存為何能奏效三、其他可以嘗試的解決辦法(一)重新檢查 URL(二)暫時關閉瀏覽器插件(三)切換網絡環…

【DeepMLF】具有可學習標記的多模態語言模型,用于情感分析中的深度融合

這是一篇我完全看不懂的論文,寫的好晦澀,適合唬人,所以在方法部分我以大白話為主 abstract 在多模態情感分析(MSA)中,多模態融合已經得到了廣泛的研究,但融合深度和多模態容量分配的作用還沒有得到充分的研究。在這項工作中,我們將融合深度、可擴展性和專用多模容量作…

【ASP.net】在Windows 11上安裝IIS并測試C# Web項目的踩坑實錄

摘要 多年未接觸.NET技術棧的田辛老師&#xff0c;最近因項目需求重新搭建測試環境。本文記錄了Windows 11環境下安裝IIS服務的全過程&#xff0c;以及一個讓開發者抓狂的“空白頁面”問題的解決方案。 1. 基礎環境配置 工欲善其事&#xff0c;必先利其器。本次環境搭建選擇…

【IP101】圖像特征提取技術:從傳統方法到深度學習的完整指南

&#x1f31f; 特征提取魔法指南 &#x1f3a8; 在圖像處理的世界里&#xff0c;特征提取就像是尋找圖像的"指紋"&#xff0c;讓我們能夠識別和理解圖像的獨特性。讓我們一起來探索這些神奇的特征提取術吧&#xff01; &#x1f4da; 目錄 基礎概念 - 特征的"體…

HybridCLR 詳解:Unity 全平臺原生 C# 熱更新方案

HybridCLR&#xff08;原 Huatuo&#xff09;是 Unity 平臺革命性的熱更新解決方案&#xff0c;它通過擴展 Unity 的 IL2CPP 運行時&#xff0c;實現了基于原生 C# 的完整熱更新能力。下面從原理到實踐全面解析這一技術。 一、核心原理剖析 1. 技術架構 原始 IL2CPP 流程&am…

機器學習——邏輯回歸ROC練習

一、 題目要求&#xff1a; 給定以下二分類模型的預測結果&#xff0c;手動繪制ROC曲線并計算AUC值&#xff1a; y_true [0, 1, 0, 1, 0, 1] # 真實標簽&#xff08;0負類&#xff0c;1正類&#xff09; y_score [0.2, 0.7, 0.3, 0.6, 0.1, 0.8] # 模型預測得分 代碼展示…

Python項目源碼69:Excel數據篩選器1.0(tkinter+sqlite3+pandas)

功能說明&#xff1a;以下是一個使用Tkinter和Pandas實現的完整示例&#xff0c;支持Excel數據讀取、雙表格展示和高級條件篩選功能&#xff1a; 1.文件操作&#xff1a;點擊"打開文件"按鈕選擇Excel文件&#xff08;支持.xlsx和.xls格式&#xff09;&#xff0c;自…

php8 枚舉使用教程

簡介 PHP 從 8.1 開始原生支持枚舉&#xff08;enum&#xff09;&#xff0c;這是 PHP 向類型安全和現代語言特性邁進的重要一步。枚舉可以定義一組有窮的、不可變的常量集合&#xff0c;常用于表示狀態值、選項類型等。 基礎語法 PHP 支持兩種類型的枚舉&#xff1a; 純枚…

【Linux】Linux環境基礎開發工具

前言 本篇博客我們來了解Linux環境下一些基礎開發工具 &#x1f493; 個人主頁&#xff1a;zkf& ? 文章專欄&#xff1a;Linux 若有問題 評論區見&#x1f4dd; &#x1f389;歡迎大家點贊&#x1f44d;收藏?文章 目錄 1.Linux 軟件包管理器 yum 2.Linux開發工具 2.1…

vue2開發者sass預處理注意

vue2開發者sass預處理注意 sass的預處理器&#xff0c;早年使用node-sass&#xff0c;也就是vue2最初默認的編譯器。 sass官方推出了dart-sass來替代。 node-sass已經停維很久了。 vue3默認使用的是dart-sass。 Uniapp的官方文檔截圖 從 HBuilderX 4.56 &#xff0c;vue2 …

Spring MVC Controller 方法的返回類型有哪些?

Spring MVC Controller 方法的返回類型非常靈活&#xff0c;可以根據不同的需求返回多種類型的值。Spring MVC 會根據返回值的類型和相關的注解來決定如何處理響應。 以下是一些常見的 Controller 方法返回類型&#xff1a; String: 最常見的類型之一&#xff0c;用于返回邏輯…

[ctfshow web入門] web55

信息收集 這里把小寫字母都過濾了&#xff0c;眾所周知linux是大小寫區分的&#xff0c;沒有小寫字母根本整不出來命令 if(isset($_GET[c])){$c$_GET[c];if(!preg_match("/\;|[a-z]|\|\%|\x09|\x26|\>|\</i", $c)){system($c);} }else{highlight_file(__FILE…

2021-11-11 C++泰勒sin(x)以2步進乘方除以階乘加減第N項

緣由c書本題&#xff0c;求解了&#xff0c;求解-編程語言-CSDN問答 int n 10, d 3, z -1; double x 2.5, xx x;while (n){xx (乘方(x, d) / 階乘(d)) * z;d 2, --n, z * -1;}std::cout << xx << std::endl;

湖倉一體化介紹

目錄 一、湖倉一體化的定義與核心概念 二、湖倉一體化出現的背景 (一)數據倉庫的局限性 (二

倉頡編程語言快速入門:從零構建全場景開發能力

在萬物互聯的智能時代,編程語言的演進始終與計算范式的革新緊密相連。華為推出的倉頡編程語言(Cangjie Programming Language)以“原生智能化、天生全場景”為核心理念,為開發者提供了一種兼顧高效開發與極致性能的新選擇。本文將帶你從零開始,快速掌握這門面向未來的語言…

AI教你學VUE——Deepseek版

一、基礎階段&#xff1a;打好Web開發基礎 HTML/CSS基礎 學習HTML標簽語義化、CSS布局&#xff08;Flex/Grid&#xff09;、響應式設計&#xff08;媒體查詢、REM/VW單位&#xff09;。資源推薦&#xff1a; MDN Web文檔&#xff08;免費&#xff09;&#xff1a;HTML | CSS實戰…