深入理解pthread多線程編程:從基礎到生產者-消費者模型

前言

在多核處理器普及的今天,多線程編程已成為提高程序性能的重要手段。POSIX線程(pthread)是Unix/Linux系統下廣泛使用的多線程API。本文將系統介紹pthread的關鍵概念,包括線程初始化、死鎖預防、遞歸鎖使用,并通過一個完整的生產者-消費者模型示例展示多線程同步的實際應用。

一、pthread基礎與靜態初始化

1.1 pthread的兩種初始化方式

pthread提供了兩種初始化互斥鎖的方式:

動態初始化:

?

pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);

靜態初始化(推薦):

?

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

靜態初始化的優勢在于:

  • 代碼更簡潔

  • 線程安全

  • 編譯期即完成初始化

1.2 靜態初始化的內部實現

PTHREAD_MUTEX_INITIALIZER實際上是一個宏定義,展開后會對互斥鎖的所有字段進行初始化。這種方式避免了運行時調用初始化函數的開銷。

二、死鎖分析與預防

2.1 死鎖產生的四個必要條件

  1. 互斥條件:資源一次只能被一個線程占用

  2. 占有并等待:線程持有資源并等待其他資源

  3. 不可搶占:資源只能由持有者釋放

  4. 循環等待:多個線程形成等待環路

2.2 典型死鎖示例

?

// 線程A
pthread_mutex_lock(&mutex1);
pthread_mutex_lock(&mutex2);
// ...
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);// 線程B
pthread_mutex_lock(&mutex2);
pthread_mutex_lock(&mutex1);
// ...
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);

2.3 死鎖預防策略

  1. 固定加鎖順序:所有線程按相同順序獲取鎖

  2. 使用trylockpthread_mutex_trylock避免阻塞

  3. 超時機制pthread_mutex_timedlock

  4. 鎖層次結構:為鎖定義嚴格的獲取層次

三、遞歸互斥鎖

3.1 為什么需要遞歸鎖?

當同一線程需要多次獲取同一個鎖時,普通互斥鎖會導致死鎖。遞歸互斥鎖允許同一線程多次加鎖。

3.2 遞歸鎖使用示例

?

pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);pthread_mutex_t mutex;
pthread_mutex_init(&mutex, &attr);void recursive_function() {pthread_mutex_lock(&mutex);// 可以安全地再次調用需要同一鎖的函數pthread_mutex_unlock(&mutex);
}

四、信號量與生產者-消費者模型

4.1 信號量基礎

信號量是一種更靈活的同步機制,核心操作:

  • sem_wait():P操作,信號量減1

  • sem_post():V操作,信號量加1

4.2 生產者-消費者模型實現

以下是經過完善的實現代碼:

?

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>#define BUFFER_SIZE 10
#define THREAD_NUM 4sem_t semEmpty, semFull;
pthread_mutex_t mutexBuffer;
int buffer[BUFFER_SIZE];
int count = 0;
int should_stop = 0;void* producer(void* args) {while (!should_stop) {int x = rand() % 100;sem_wait(&semEmpty);pthread_mutex_lock(&mutexBuffer);buffer[count] = x;count++;printf("Produced %d\n", x);pthread_mutex_unlock(&mutexBuffer);sem_post(&semFull);sleep(1);}return NULL;
}void* consumer(void* args) {while (!should_stop) {int y;sem_wait(&semFull);pthread_mutex_lock(&mutexBuffer);if (count > 0) {y = buffer[count - 1];count--;printf("Consumed %d\n", y);}pthread_mutex_unlock(&mutexBuffer);sem_post(&semEmpty);sleep(1);}return NULL;
}int main() {srand(time(NULL));pthread_t th[THREAD_NUM];// 初始化同步對象sem_init(&semEmpty, 0, BUFFER_SIZE);sem_init(&semFull, 0, 0);pthread_mutex_init(&mutexBuffer, NULL);// 創建線程for (int i = 0; i < THREAD_NUM; i++) {if (i % 2 == 0) {pthread_create(&th[i], NULL, producer, NULL);} else {pthread_create(&th[i], NULL, consumer, NULL);}}// 運行20秒后停止sleep(20);should_stop = 1;// 等待線程結束for (int i = 0; i < THREAD_NUM; i++) {pthread_join(th[i], NULL);}// 清理資源sem_destroy(&semEmpty);sem_destroy(&semFull);pthread_mutex_destroy(&mutexBuffer);return 0;
}

4.3 關鍵點解析

  1. 雙信號量設計

    • semEmpty:緩沖區空位數量,初始為緩沖區大小

    • semFull:緩沖區數據數量,初始為0

  2. 互斥鎖保護

    • 確保對緩沖區的操作是原子的

  3. 終止機制

    • 使用should_stop標志優雅停止線程

  4. 邊界檢查

    • 消費者檢查count > 0避免緩沖區下溢

五、常見問題與調試技巧

  1. 線程不退出

    • 確保所有線程都有退出條件

    • 檢查是否有線程阻塞在同步原語上

  2. 數據競爭

    • 使用工具如Valgrind的Helgrind檢測

    • 確保所有共享數據都有適當的保護

  3. 性能優化

    • 減少臨界區范圍

    • 考慮讀寫鎖替代互斥鎖

結語

多線程編程既強大又復雜。通過合理使用pthread提供的同步原語,可以構建高效可靠的并發程序。生產者-消費者模型是多線程編程的經典范式,理解其實現原理對掌握并發編程至關重要。

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

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

相關文章

springboot 對接馬來西亞數據源API等多個國家的數據源

使用Spring Boot對接StockTV全球金融數據API指南 StockTV提供了覆蓋股票、外匯、期貨和加密貨幣的全球化金融數據接口。本文將通過Spring Boot實現對這些API的快速對接&#xff0c;并提供完整的代碼示例。 一、前期準備 1. 獲取API Key 訪問StockTV官網聯系客服獲取API Key…

軟件測試常用設計模式

設計模式的重要原則就是&#xff1a;高內聚、低耦合&#xff1b;通常程序結構中各模塊的內聚程度越高&#xff0c;模塊間的耦合程度就越低。 數據驅動測試&#xff1a;Data Driven Testing&#xff0c;簡稱DDT&#xff1b; 數據驅動指的是從數據文件&#xff08;如數據庫、Ex…

基于 Fluent-Bit 和 Fluentd 的分布式日志采集與處理方案

#作者&#xff1a;任少近 文章目錄 需求描述系統目標系統組件Fluent BitFluentdKafka 數據流與處理流程日志采集日志轉發到 Fluentd日志處理與轉發到 KafkaKafka 作為消息隊列 具體配置Fluent-Bit的CM配置Fluent-Bit的DS配置Fluentd的CM配置Fluentd的DS配置Kafka查詢結果 需求…

正則表達式(Regular Expression,簡稱 Regex)

一、5w2h&#xff08;七問法&#xff09;分析正則表達式 是的&#xff0c;5W2H 完全可以應用于研究 正則表達式&#xff08;Regular Expressions&#xff09;。通過回答 5W2H 的七個問題&#xff0c;我們可以全面理解正則表達式的定義、用途、使用方法、適用場景等&#xff0c…

爬蟲獲取1688關鍵字搜索接口的實戰指南

在當今電商行業競爭激烈的環境下&#xff0c;數據的重要性不言而喻。1688作為國內領先的B2B電商平臺&#xff0c;擁有海量的商品信息&#xff0c;這些數據對于商家的市場分析、選品決策、價格策略制定等都有著重要的價值。本文將詳細介紹如何通過爬蟲技術獲取1688關鍵字搜索接口…

如何快速解決django存儲session變量時出現的django.db.utils.DatabaseError錯誤

我們在學習django進行web編程的時候&#xff0c;有時需要將一些全局變量信息存儲在session中&#xff0c;但使用過程中&#xff0c;卻發現會引起數據庫的報錯。通過查看django源碼信息&#xff0c;發現其對session信息進行了ORM映射&#xff0c;如果數據庫中不存在對應的表信息…

C語言復習--assert斷言

assert.h 頭?件定義了宏 assert() &#xff0c;?于在運?時確保程序符合指定條件&#xff0c;如果不符合&#xff0c;就報錯終止運行。這個宏常常被稱為“斷?”。 assert(p ! NULL); 代碼在程序運?到這??語句時&#xff0c;驗證變量 p 是否等于 NULL 。如果確實不等于 NU…

STL新增內容

文章目錄 C11 中的 STL 新增內容容器算法 C14 中的 STL 新增內容容器算法 C17 中的 STL 新增內容容器算法 C20 中的 STL 新增內容容器算法 C11 中的 STL 新增內容 容器 std::array&#xff1a;這是一個固定大小的數組容器&#xff0c;和原生數組類似&#xff0c;但具備更好的…

C#測試Excel開源組件ExcelDataReader

使用微軟的com組件Microsoft.office.Interop.Excel讀寫Excel文件雖然可用&#xff0c;但是列多、行多的時候速度很慢&#xff0c;之前測試過Sylvan.Data.Excel包的用法&#xff0c;如果只是讀取Excel文件內容的話&#xff0c;還可以使用ExcelDataReader包&#xff0c;后者是C#開…

位置編碼匯總 # 持續更新

看了那么多還沒有講特別好的&#xff0c;GPT老師講的不錯關于三角函數編碼。 一、 手撕transformer常用三角位置編碼 GPT說&#xff1a;“低維度的編碼&#xff08;例如&#xff0c;第一個維度&#xff09;可以捕捉到大的位置差異&#xff0c;而高維度的編碼則可以捕捉到小的細…

Java 模塊系統深度解析

Java 模塊系統深度解析 Java 模塊系統&#xff08;Java Platform Module System, JPMS&#xff09;是 Java 9 引入的一項重要特性&#xff0c;它從根本上改變了 Java 應用程序的打包和依賴管理方式。本文將全面介紹 Java 模塊系統的核心概念、優勢及實際應用。 一、為什么需要…

藍橋杯杯賽-日期模擬

知識點 處理日期 1. 按天枚舉日期&#xff1a;逐天遍歷起始日期到結束日期范圍內的每個日期。 2. 處理閏年&#xff1a;正確判斷閏年條件。閏年定義為&#xff1a;年份 滿足以下任意一個條件&#xff1a;(閏年的2月只有29天) 滿足下面一個條件就是閏年 1> 是 400 的倍數…

.Net中對稱加密的實現

常見對稱加密算法及優缺點 1. DES&#xff08;Data Encryption Standard&#xff09; 優點&#xff1a;是最早被廣泛應用的加密算法&#xff0c;算法公開&#xff0c;實現簡單&#xff0c;效率較高。缺點&#xff1a;密鑰長度較短&#xff08;56 位&#xff09;&#xff0c;在…

SQLMesh調度系統深度解析:內置調度與Airflow集成實踐

本文系統解析SQLMesh的兩種核心調度方案&#xff1a;內置調度器與Apache Airflow集成。通過對比兩者的適用場景、架構設計和操作流程&#xff0c;為企業構建可靠的數據分析流水線提供技術參考。重點內容包括&#xff1a; 內置調度器的輕量級部署與性能優化策略Airflow集成的端到…

centos線程數查看

查看當前最大支持的線程數 cat /proc/sys/kernel/threads-max當前用戶進程可以創建的最大線程數&#xff08;包括子進程&#xff09; [rootlocalhost tmp]# ulimit -u得到當前實際的線程數 [rootlocalhost tmp]# ps -eLf | wc -l統計每個進程的總線程數前20的數據 [rootloc…

【大模型】視覺語言模型:Qwen2.5-VL的使用

官方github地址&#xff1a;https://github.com/QwenLM/Qwen2.5-VL 目錄 Qwen家族的最新成員&#xff1a;Qwen2.5-VL 主要增強功能 模型架構更新 快速開始 使用Transformers聊天 Docker Qwen家族的最新成員&#xff1a;Qwen2.5-VL 主要增強功能 強大的文檔解析功能&am…

HDMI接口設計

1. HDMI簡介 HDMI(High Definition Multimedia Interface)高清多媒體接口,是首個支持在單線纜上傳輸,不經過壓縮的全數字高清晰度、多聲道音頻和智能格式與控制命令數據的數字接口。這個接口可以同時傳輸視頻信號、音頻信號和控制信號。 從上圖里面可以看到HDMI有3組數據信號…

C/C++ JSON 庫綜合對比及應用案例(六)

第六部分&#xff1a;C/C JSON 庫綜合對比及應用案例 &#x1f4e2; 快速掌握 JSON&#xff01;文章 視頻雙管齊下 &#x1f680; 如果你覺得閱讀文章太慢&#xff0c;或者更喜歡 邊看邊學 的方式&#xff0c;不妨直接觀看我錄制的 JSON 課程視頻&#xff01;&#x1f3ac; …

LXC 導入(Rockylinux,almalinux,oraclelunx,debian,ubuntu,openEuler,kail,opensuse)

前提要求 ubuntu下安裝lxd 參考Rockylinux下安裝lxd 參考LXC 源替換參考LXC 容器端口發布參考LXC webui 管理<

Spring MVC 頁面跳轉方案與區別

SpringMVC 的頁面跳轉方案主要分為 ?轉發&#xff08;Forward&#xff09;? 和 ?重定向&#xff08;Redirect&#xff09;? 兩類&#xff0c;具體實現方式和區別如下&#xff1a; 一、頁面跳轉方案 1. ?轉發&#xff08;Forward&#xff09;? 默認方式?&#xff1a;直…