c語言中實現線程同步的操作

線程

常見問題

同步權限

在多線程 / 多進程并發時,為避免共享資源(如內存變量、硬件設備、文件)被同時修改導致的數據不一致,需要通過 “同步機制” 控制誰能訪問資源 ——“獲取同步權限” 就是線程 / 進程申請這種訪問資格的過程。

v4 = _InterlockedCompareExchange(a1, 1, 0);
  • 第一個參數 a1:指向目標內存地址的指針(通常是一個共享變量,如 LONG* 類型),即要操作的 “共享資源標記”。
  • 第二個參數 1:當比較成功時,要寫入目標內存地址的 “新值”。
  • 第三個參數 0:“預期值”,即我們認為目標內存當前應該有的值。
  • 返回值是操作前目標內存地址(a1 指向的地址)中的原始值
// 定義一個共享的“鎖標記”,0表示未占用,1表示已占用
LONG lock_flag = 0;// 線程A嘗試獲取鎖
LONG original = _InterlockedCompareExchange(&lock_flag, 1, 0);
if (original == 0) {//獲取到權限// 執行臨界區操作...// 操作完成后釋放鎖(如將lock_flag設回0)
} else {// 獲取鎖失敗(鎖已被其他線程占用)// 可選擇等待、重試或放棄
}

臨界區

臨界區(Critical Section) 指的是一段 “不能被多個線程同時執行” 的代碼片段,當一個線程正在執行臨界區代碼時,其他線程必須等待該線程執行完畢,才能進入同一臨界區。

原子性

“原子”(Atomic)描述的是一個不可分割、不可中斷的操作單元。一個 “原子操作” 要么完整地執行完畢,要么完全不執行,中間不會被任何其他線程、進程或中斷打斷,不存在 “執行到一半” 的中間狀態。

假設兩個線程(Thread A、Thread B)同時對共享變量 count(初始值為 0)執行 count += 1 操作。
在底層會拆分為 3 個 CPU 指令:

  1. 從內存讀取 count 的值到 CPU 寄存器(如 mov eax, [count]);
  2. 寄存器中的值加 1(如 inc eax);
  3. 將寄存器的值寫回內存(如 mov [count], eax)。

如果操作不原子,可能出現以下 “交錯執行”:

  • Thread A 執行步驟 1:讀取 count=0 到寄存器;
  • 此時 CPU 切換到 Thread B,Thread B 執行步驟 1-3:讀取 count=0 → 加 1→ 寫回 count=1
  • CPU 切回 Thread A,繼續執行步驟 2-3:寄存器值加 1(0→1)→ 寫回 count=1

最終 count 的結果是 1,但預期是 2。

原子操作的本質

“原子性” 需要硬件(CPU)提供底層支持,再配合軟件(操作系統、編程語言庫)封裝成易用的接口。

硬件層:CPU 的原子指令支持
不同架構的 CPU 會提供專門的 “原子操作指令”,確保單個指令的不可分割性:

  • x86/x86_64 架構:通過 lock 前綴實現原子性(如 lock cmpxchglock inc)。lock 前綴會讓 CPU 在執行指令期間 “鎖定系統總線”,阻止其他 CPU 核心同時訪問該內存地址,確保指令執行不被打斷;
  • ARM 架構:提供 ldrex(原子加載)、strex(原子存儲)等指令,通過 “獨占訪問內存” 機制實現原子性;
  • RISC-V 架構:通過 amoswap.wamoadd.w 等 “原子內存操作指令”(AMO 指令)實現原子性。

這些硬件指令是 “原子操作” 的基石 —— 軟件層面的原子接口(如 C++ 的 std::atomic、Windows 的 _InterlockedXXX)本質都是對這些 CPU 指令的封裝。

軟件層:原子操作的封裝與擴展
硬件指令通常只支持 “單個內存地址的簡單操作”(如加 1、比較交換),軟件會在此基礎上封裝更靈活的原子操作:

原子操作

原子的交換操作
_InterlockedExchange(state_lock, 2);
  • 第一個參數 state_lock:指向目標內存地址的指針(通常是一個共享變量,如 LONG* 類型),即要被修改的 “狀態標記”。
  • 第二個參數 2:要寫入目標內存地址的 “新值”。
  • 返回值:原子地將 state_lock 指向的內存值更新為 2,同時返回該內存地址在更新前的原始值
原子比較交換操作
_InterlockedCompareExchange(&lock_flag, 1, 0);

參數1:類型為 volatile LONG*(指向 32 位有符號整數的指針),要操作的目標內存地址
參數2:類型為 LONG(32 位有符號整數),比較成功后要寫入目標地址的值
參數3:類型為 LONG,表示預期的目標地址當前值(即 “舊值”)。
返回值:返回值為 LONG 類型,即目標地址在操作執行前的原始值

// 定義一個共享的“鎖標記”,0表示未占用,1表示已占用
LONG lock_flag = 0;// 線程A嘗試獲取鎖
LONG original = _InterlockedCompareExchange(&lock_flag, 1, 0);
if (original == 0) {//獲取到權限// 執行臨界區操作...// 操作完成后釋放鎖(如將lock_flag設回0)
} else {// 獲取鎖失敗(鎖已被其他線程占用)// 可選擇等待、重試或放棄
}
原子地將目標變量的值加
_InterlockedIncrement(dword_14002BFF0);

參數dword_14002BFF0 是一個 LONG 類型(32 位)的共享變量(通常是全局或多線程可見的變量),表示要進行遞增操作的目標。

返回值:函數返回遞增后的新值LONG 類型)。

原子減 1
_InterlockedDecrement(a1)

參數:類型為 volatile LONG*(指向 32 位有符號整數的指針)
返回值:類型為 LONG(32 位有符號整數),表示減 1 操作完成后的結果值(即 *a1 - 1 的結果)。

線程同步

SRW 鎖

SRW 鎖支持兩種獲取模式:

  • 共享模式(Shared Mode):多個線程可同時獲取,適用于 “只讀操作” 場景(多個讀者可并行訪問資源)。
  • 獨占模式(Exclusive Mode):僅允許一個線程獲取,適用于 “修改操作” 場景(寫者需獨占資源)。
初始化SRW
void InitializeSRWLock(PSRWLOCK SRWLock
);

參數:PSRWLOCKSRWLOCK* 的類型別名,指向 SRWLOCK 結構體(輕量級讀寫鎖的核心數據結構)。

核心作用:初始化 SRW 鎖對象的內部狀態,初始化后的 SRW 鎖可通過以下函數實現讀寫分離的同步

  • 讀操作:AcquireSRWLockShared(獲取共享鎖)和 ReleaseSRWLockShared(釋放共享鎖)。
  • 寫操作:AcquireSRWLockExclusive(獲取獨占鎖)和 ReleaseSRWLockExclusive(釋放獨占鎖)。
讀操作
void AcquireSRWLockShared(PSRWLOCK SRWLock
);

參數 SRWLock 是指向 SRWLOCK 結構體的指針,讓線程以 “共享模式”(只讀模式)安全地獲取輕量級讀寫鎖(SRW Lock)

void ReleaseSRWLockShared(PSRWLOCK SRWLock
);

參數 SRWLock 是指向 SRWLOCK 結構體的指針(PSRWLOCKSRWLOCK*),表示要釋放的輕量級讀寫鎖對象。

寫操作
void AcquireSRWLockExclusive(PSRWLOCK SRWLock
);

讓線程以 “獨占模式” 獲取 SRW 鎖,確保對共享資源的修改操作(寫操作)具有原子性

臨界區

進出臨界區
EnterCriticalSection(&stru_14002C030)

參數:類型LPCRITICAL_SECTION(即 CRITICAL_SECTION*,指向臨界區結構體的指針)。
無返回值
作用:讓當前線程 “獲取臨界區的訪問權”

LeaveCriticalSection(&stru_14002C030);

作用:釋放臨界區

初始化臨界區
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection
);

參數 lpCriticalSection 是指向 CRITICAL_SECTION 結構體的指針(LPCRITICAL_SECTIONCRITICAL_SECTION* 的類型別名),表示要初始化的臨界區對象。

異常:如果初始化失敗(通常是由于系統資源不足),函數會觸發一個異常(而非返回錯誤碼)。因此在實際使用中,可能需要配合異常處理(如 __try/__except)捕獲潛在錯誤。

作用:初始化臨界區對象的內部狀態,初始化后的臨界區可通過 EnterCriticalSection(進入臨界區)和 LeaveCriticalSection(離開臨界區)實現。

使用示例

// 定義臨界區對象(全局或棧上)
CRITICAL_SECTION CriticalSection;// 初始化臨界區(通常在程序啟動或模塊初始化時調用)
InitializeCriticalSection(&CriticalSection);// 多線程場景中使用
void ThreadFunc() {// 進入臨界區(獲取同步權限)EnterCriticalSection(&CriticalSection);// 執行需要同步的操作(如訪問共享資源)AccessSharedResource();// 離開臨界區(釋放同步權限)LeaveCriticalSection(&CriticalSection);
}// 程序退出前銷毀臨界區(釋放資源)
DeleteCriticalSection(&CriticalSection);
臨界區同步
TryEnterCriticalSection()

參數:LPCRITICAL_SECTION lpCriticalSection指向 CRITICAL_SECTION 結構體的指針(與 EnterCriticalSection 相同),表示要嘗試進入的臨界區對象,需提前通過 InitializeCriticalSection 初始化。

返回值: TRUE(非 0 值):表示成功進入臨界區,FALSE(0):表示未能進入臨界區

作用:非阻塞嘗試進入臨界區,與 EnterCriticalSection 的 “阻塞等待” 不同,TryEnterCriticalSection 的核心特點是 “嘗試進入,失敗立即返回”,適用于以下場景:

  • 當線程只需 “短暫嘗試” 獲取臨界區,若失敗則執行其他任務(而非等待),避免線程阻塞。
  • 實現 “超時等待” 邏輯(結合循環和 Sleep,多次嘗試后放棄)。
臨界區狀態結構體
00000000 struct _RTL_CRITICAL_SECTION // sizeof=0x28
00000000 {                                       // XREF: .data:CriticalSection/r
00000000                                         // .data:__rtl_critical_section/r
00000000     PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
00000008     LONG LockCount;
0000000C     LONG RecursionCount;
00000010     HANDLE OwningThread;
00000018     HANDLE LockSemaphore;
00000020     ULONG_PTR SpinCount;
00000028 };

線程調度

CPU 時間片

SwitchToThread() 

返回值:

  • 返回 TRUE(非 0 值):表示當前有其他 “就緒狀態” 的線程(屬于同一優先級或更高優先級)被調度執行;
  • 返回 FALSE(0):表示當前沒有其他就緒線程可調度(即系統中只有當前線程可運行)。

作用:

  • 當前線程主動放棄剩余的 CPU 時間片,讓操作系統調度器重新選擇一個就緒線程(通常是同優先級的其他線程)運行。

自旋等待

while ( 1 )
{v6 = _InterlockedCompareExchange(a1, 1, 0); // 原子比較交換:嘗試將a1從0改為1if ( !v6 ) // v6=0表示成功獲取權限(a1原本為0,已改為1)break;if ( v6 != 2 ) // 若a1當前為1(被其他線程占用),則主動讓出CPUSwitchToThread();if ( *a1 == 2 ) // 若等待期間a1變為2(終止),則返回0return 0LL;
}

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

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

相關文章

一臺設備管理多個 GitHub 賬號:從配置到切換的完整指南

一臺設備管理多個 GitHub 賬號:從配置到切換的完整指南 在日常開發中,我們經常需要在同一臺電腦上使用多個 GitHub 賬號(比如個人賬號和工作賬號)。但默認情況下,Git 會優先使用全局配置的賬號,導致推送代…

即插即用,秒入虛擬:TouchDIVER Pro 觸覺手套 賦能 AR/VR 高效交互

一、即插即用,零門檻開啟沉浸之旅 在XR(擴展現實)技術高速發展的今天,用戶對“真實感”的追求愈發迫切。Weart公司旗下旗艦產品TouchDIVER Pro觸覺手套,憑借無需適配器、無需復雜設置的極簡設計,打破傳統觸…

GitHub熱榜項目 - 日榜之應用場景與未來發展趨勢

一、引言GitHub熱榜項目 - 日榜呈現出豐富多樣的技術成果,這些項目蘊含著巨大的應用潛力,并且對未來數智化技術的發展有著重要的指示作用。深入探究其應用場景以及未來發展趨勢,能讓我們更好地把握技術發展方向,將這些前沿技術應用…

Linux網絡:socket編程TCP

文章目錄前言一,服務器端流程1-1 綁定協議1-2 綁定IP和端口1-3 監聽客戶端1-4 接收連接1-5 收發數據1-6 關閉連接1-7 服務端整體代碼二,客戶端流程2-1 指定地址和端口2-2 連接服務器2-3 發送消息2-4 客戶端整體代碼前言 TCP 的通信過程就像兩個人打電話…

飛書智能查詢機器人搭建說明文檔

飛書智能查詢機器人搭建說明文檔 一、使用手冊 1. 創建飛書機器人應用 如果僅需對接已有機器人應用則可跳過該步驟(建議各業務部門獨立使用各自的機器人應用)。在飛書開發者后臺中創建企業自建應用,添加機器人應用能力并申請對應的身份權限…

藍色系列包裝行業網站 適合企業站,帶手機版自適應

內容目錄一、詳細介紹二、效果展示1.部分代碼2.效果圖展示三、學習資料下載一、詳細介紹 藍色通用企業網站是基于SDCMS四合一企業網站管理系統開發的模板,適合企業站,帶手機版。 四網合一企業網站管理系統是一個以PHPMySQL/Sqlite進行開發的四網合一網…

【大模型:知識圖譜】--6.Neo4j DeskTop安裝+使用

上一期講了圖知識庫的安裝, 【圖數據庫】--Neo4j 安裝_neo4j安裝-CSDN博客 現在來看看可視化管理程序:Neo4j DeskTop的安裝. 需要先安裝java環境,具體看上面 目錄 1.Neo4j DeskTop版下載 2.Neo4j DeskTop版安裝 3.Neo4j DeskTop版使用 …

Python爬蟲實戰——使用NetNut網頁解鎖器獲取亞馬遜電商數據

文章目錄一、電商數據的作用1.1 支撐科學決策,降低試錯成本1.2 提升運營效率,實現降本增效1.3 深化用戶理解,驅動個性化服務1.4 監測競品動態,制定差異化策略1.5 驅動產品創新,滿足用戶需求二、爬取目標三、環境準備四…

超越NAT:如何構建高效、安全的內網穿透隧道

在敏捷開發和分布式協作成為主流的今天,開發者需要一個能夠將本地開發環境瞬間暴露給公網的能力,以便進行演示、聯調或處理回調。傳統方案如配置路由器端口映射或部署VPN,不僅繁瑣且存在安全風險。內網穿透技術,特別是以 ngrok、Z…

第二十三章 ESP32S3 RTC 實驗

本章介紹 ESP32-S3 實時時鐘(RTC)的使用,實時時鐘能為系統提供一個準確的時間,即時系統復位或主電源斷電, RTC 依然能夠運行,因此 RTC 也經常用于各種低功耗場景。通過本章的學習,將學習到 RTC …

Java 輕松實現 Markdown 轉 Word、PDF、HTML

在軟件開發和技術寫作領域,Markdown 已成為一種被廣泛使用的輕量級標記語言。它的語法簡潔,書寫效率高,非常適合快速記錄筆記、撰寫技術文檔或博客文章。但在實際應用中,Markdown 文件往往需要被轉換為更通用的格式,例…

Kafka系列之:Kafka broker does not support the ‘MetadataRequest_v0‘ Kafka protocol.

Kafka系列之:Kafka broker does not support the MetadataRequest_v0 Kafka protocol. 一、完整報錯 二、錯誤原因 三、解決方法 一、完整報錯 kafka.errors.IncompatibleBrokerVersion: IncompatibleBrokerVersion: Kafka broker does not support the ‘MetadataRequest_v0’…

開源AI紅隊工具“Red AI Range“助力發現、分析與緩解AI系統漏洞

開源AI紅隊平臺Red AI Range(RAR)正在改變安全專業人員評估和強化AI系統的方式。該平臺通過模擬真實攻擊場景,利用容器化架構和自動化工具,簡化了AI特有漏洞的發現、分析和緩解流程。**核心功能** 1. 武器庫/目標按鈕可快速啟動…

SQL 數據庫簡介

SQL(Structured Query Language)是一種用于管理和操作關系型數據庫的標準語言。關系型數據庫以表格形式存儲數據,并通過行和列的結構化方式組織信息。SQL 提供了一套強大的命令,用于查詢、插入、更新和刪除數據,以及管…

SpringBoot4與Spring7發布:云原生深度進化

Spring Boot 4和Spring Framework 7帶來基礎要求升級、模塊化改進、API版本化、聲明式HTTP客戶端、彈性注解等重大特性,標志著Java開發生態向云原生時代的深度進化。 近日,Spring生態迎來了自2022年以來最具里程碑意義的更新——Spring Boot 4和Spring …

基于Spring Boot與Micrometer的系統參數監控指南

如何為你的Spring Boot應用裝上一個功能強大的監控儀表盤在現代微服務架構中,系統監控已成為保障應用穩定性的關鍵環節。通過有效的監控,我們可以實時了解應用的運行狀態,及時發現并解決性能問題。本文將介紹如何使用Micrometer及其注冊表&am…

【運維】-- 前端會話回放與產品分析平臺之 openreplay

目錄 OpenReplay 項目分析 1、項目概覽 2、關鍵特性 3、代碼結構(Monorepo) 4、技術棧與語言占比 5、部署與交付 6、社區與支持 7、版本與活躍度(截至倉庫頁面所示) 8、適用場景 9、優勢與注意事項 10、落地建議&#…

NineData社區版 V4.5.0 正式發布!運維中心新增細粒度任務權限管理,新增MySQL至Greenplum全鏈路復制對比

NineData 社區版 V4.5.0 正式發布!在數據復制方面,新增 MySQL 至 Greenplum 全鏈路復制對比,并優化全局 DDL 管控、MySQL/PostgreSQL/MongoDB 同構性能。在數據庫 DevOps 方面,新增支持 AWS RDS 全系列及阿里云 PolarDB&#xff0…

discuz所有下載版本和升級工具

下載版本: Discuz! 每日構建版下載 - DiscuzX 3.x Daily Build Download Site SC是簡體中文 TC是繁體中文 可能你需要其他版本: Discuz!官方網站 - 開放、連接、共贏 下載簡體中文就好。 升級工具: 升級程序下載地址 https://gitee.com/oldhuhu/DiscuzX34235.git(…

【開題答辯全過程】以 “紅色棗莊”旅游網站為例,包含答辯的問題和答案

個人簡介一名14年經驗的資深畢設內行人,語言擅長Java、php、微信小程序、Python、Golang、安卓Android等開發項目包括大數據、深度學習、網站、小程序、安卓、算法。平常會做一些項目定制化開發、代碼講解、答辯教學、文檔編寫、也懂一些降重方面的技巧。感謝大家的…