Linux系統編程 day11 鎖 (兩天沒有更新了,中期完就休息了)

鎖的注意事項

1、盡量保證鎖的粒度,越小越好。(訪問共享數據前,加鎖,訪問結束后立即解鎖)

2、互斥鎖,本質是結構體,但是可以看成整數,初值為1。(pthread_mutex_init調用成功)

3、加鎖: --操作,阻塞線程

4、解鎖:++操作,喚醒阻塞在鎖上的進程

try鎖:嘗試加鎖 , 成功-- , 失敗:返回錯誤號(EBUSY),不阻塞。

讀寫鎖(三句話)

讀寫鎖只有一把,但是具備兩種狀態。

“寫模式加鎖”:解鎖前,所有對該鎖加鎖的線程都會被阻塞。

“讀模式加鎖”:其他線程用讀模式加鎖會成功,寫模式加鎖則阻塞。

“讀模式加鎖”:既有試圖寫模式加鎖的線程也有讀模式加鎖的線程,那么讀寫鎖會阻塞隨后的讀模式鎖請求,優先滿足寫模式鎖,讀寫鎖并行阻塞寫鎖優先級高。

寫獨占、讀共享

相較于互斥量而言,當讀線程多的時候,提高訪問效率。鎖一般都定義成全局變量。

主要應用函數pthread_rwlock_xxx

pthread_rwlock_t rwlock;   創建讀寫鎖pthread_rwlock_init
pthread_rwlock_destroy
pthread_rwlock_rdlock
pthread_rwlock_wrlock
pthread_rwlock_tryrdlock
pthread_rwlock_trywrlock
pthread_rwlock_unlock成功返回0,失敗返回錯誤號

?3個線程不定時寫同一全局資源 , 5個線程不定時讀同一全局資源.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>pthread_rwlock_t rwlock;
int counter = 20;void* th_write(void* arg){int t;int ret;int i = (int)arg;while(1){ret = pthread_rwlock_wrlock(&rwlock);if(ret != 0){fprintf(stderr , "pthread_rwlock_wrlock error with :%s\n" , strerror(ret));exit(1);}t = counter;usleep(1000);printf("-----write %d: %lu: counter=%d , counter++ = %d\n" , i , pthread_self() , t , ++counter);ret = pthread_rwlock_unlock(&rwlock);if(ret != 0){fprintf(stderr , "pthread_rwlock_unlock error with :%s\n" , strerror(ret));exit(1);}usleep(10000);}return NULL;
}void* th_read(void* arg){int i = (int)arg;int ret;while(1){ret = pthread_rwlock_rdlock(&rwlock);if(ret != 0){fprintf(stderr , "pthread_rwlock_rdlock error with :%s\n" , strerror(ret));exit(1);}printf("-------read %d: %lu: counter = %d\n", i , pthread_self() , counter);ret = pthread_rwlock_unlock(&rwlock);if(ret != 0){fprintf(stderr , "pthread_rwlock_unlock error with :%s\n" , strerror(ret));exit(1);}usleep(2000);}return NULL;
}int main(int argc , char *argv[])
{pthread_t tid[8];int ret ;int i ;ret = pthread_rwlock_init(&rwlock , NULL);if(ret != 0){fprintf(stderr , "pthread_rwlock_init error with :%s\n" , strerror(ret));exit(1);}for(i =0 ; i < 3 ; i++ ){ret = pthread_create(&tid[i], NULL , th_write ,(void*)i);if(ret != 0){fprintf(stderr , "pthread_create error with :%s\n" , strerror(ret));exit(1);}}for(i = 0 ; i < 5 ; i++){ret = pthread_create(&tid[i+3] , NULL , th_read , (void*)i);if(ret != 0){fprintf(stderr , "pthread_create error with :%s\n" , strerror(ret));exit(1);}}for(i = 0 ; i < 8 ; i++){pthread_join(&tid[i] , NULL);if(ret != 0){fprintf(stderr , "pthread_join error with :%s\n" , strerror(ret));exit(1);}}pthread_rwlock_destroy(&rwlock);return 0 ;
}

死鎖

使用鎖不恰當導致的現象。條件

1、線程試圖對同一個互斥量加鎖兩次

2、線程1擁有A鎖 , 請求獲得B鎖 , 線程2擁有B鎖,請求獲得A鎖。

條件變量

本身不是鎖,但是通常要結合鎖來使用。(等待某一個變量滿足)。

pthread_cond_t cond;   創建讀寫鎖pthread_cond_init
pthread_cond_destroy
pthread_cond_wait
pthread_cond_timewait
pthread_cond_signal
pthread_cond_broadcast成功返回0,失敗返回錯誤號

pthread_cond_wait函數

阻塞等待一個條件變量

int pthread_cond_wait(&cond , pthread_mutex_t *restrict murex);

函數作用:
1、阻塞等待條件變量cond滿足。

2、釋放已掌握的互斥鎖(解鎖),相當于pthread_mutex_unlock(&mutex);? ?1,2兩步為一個原子操作

3、當被喚醒,pthread_cond_wait函數返回時,解除阻塞并重新申請獲取互斥鎖Pthread_mutex_lock(&mutex).

?生產者、消費者模型

pthread_cond_signal函數

喚醒阻塞在條件變量上的一個線程

pthread_cond_broadcast函數

喚醒阻塞在條件變量上的多個線程

要求借助條件變量完成生產者消費者模型

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>struct msg{int num;struct msg* next;
};pthread_mutex_t mutex;
pthread_cond_t has_data = PTHREAD_COND_INITIALIZER;struct msg * head;void err_thread(int ret , char*str)
{if(ret != 0){fprintf(stderr , "%s:%s\n" ,str ,  strerror(ret));pthread_exit(NULL);}
}void* produser(void* arg)
{int ret ;while(1){struct msg *mp;mp = malloc(sizeof(struct msg));mp->num = rand() % 1000 + 1;pthread_mutex_lock(&mutex);if(ret != 0)err_thread(ret , "pthread_mutex_lock error");head = mp;printf("------prod:%d , pid:%lu\n" , mp->num , pthread_self());pthread_mutex_unlock(&mutex);if(ret != 0)err_thread(ret , "pthread_mutex_unlock error");ret = pthread_cond_signal(&has_data);if(ret != 0)err_thread(ret , "pthread_mutex_lock error");sleep(rand() % 3);}return NULL;
}void* consumer(void* arg)
{int ret;while(1){struct msg *mp;mp = head;ret = pthread_mutex_lock(&mutex);if(ret != 0)err_thread(ret , "pthread_mutex_lock error");while(head == NULL){ret = pthread_cond_wait(&has_data , &mutex);  // pthread_cond_wait函數返回時,重新加鎖mutex。if(ret != 0 )err_thread(ret , "pthread_con_wait error");}mp = head;head = mp->next;ret = pthread_mutex_unlock(&mutex);if(ret != 0)err_thread(ret , "pthread_mutex_lock error");printf("-------cons:%d , cid:%lu\n" , mp->num , pthread_self());sleep(rand()%3);free(mp);}return NULL;
}int main(int argc , char *argv[])
{pthread_t pid , cid;srand(time(NULL));int ret;ret = pthread_mutex_init(&mutex , NULL);if(ret != 0)err_thread(ret , "pthread_create error");ret = pthread_create(&pid , NULL , produser , NULL);if(ret != 0)err_thread(ret , "pthread_create error");ret = pthread_create(&cid , NULL , consumer , NULL);if(ret != 0)err_thread(ret , "pthread_create error");pthread_join(pid , NULL);pthread_join(cid , NULL);return 0 ;
}

信號量

應用于線程、進程間同步。

相當于初始化值為 N?的互斥量。 N 為可同時訪問的進程數。

sem_init()
sem_destroy() 
sem_wait()   //相當于加鎖
sem_trywait()
sem_timewait()
sem_post()   //相當于解鎖返回值:成功返回0 , 失敗返回errno

sem_wait 信號量>0,信號量-- ;信號量 = 0 ,線程阻塞。? 對比pthread_mutex_lock()。?

sem_post 信號量++, 喚醒阻塞在信號量上的線程

信號量的初值,決定了占用信號量的線程個數。

?sem_init()函數

int sem_init(sem_t *sem , int pshared , unsigned int value);pshared:  0:用于線程間同步1:用于進程間同步value :N值 (指定同時訪問的數目)

信號量的生產者消費者模型

?

主要是明白wait和post的關系,以及阻塞在哪的關系。?

重點:

sem_wait 信號量>0,信號量-- ;信號量 = 0 ,線程阻塞。? 對比pthread_mutex_lock()。?

sem_post 信號量++, 喚醒阻塞在信號量上的線程

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
#define NUM 5
int queue[NUM];
sem_t blank_number;
sem_t stat_number;void err_thread(int ret , char*str)
{if(ret != 0 ){fprintf(stderr , "%s:%s\n" , str , strerror(ret));pthread_exit(NULL);}
}void* produser(void* arg)
{int i = 0;while(1){sem_wait(&blank_number);//空格子數--queue[i] = rand()%1000 + 1;printf("---produce:%d\n" , queue[i]);sem_post(&stat_number); // 產品數++i = (i+1) % NUM; // 借助隊列下標實現環形隊列sleep(rand()%1);}return NULL;
}void* consumer(void* arg)
{int i = 0;while(1){sem_wait(&stat_number);printf("-----consume:%d\n" , queue[i]);queue[i] = 0;sem_post(&blank_number);i = (i + 1)% NUM;sleep(rand()%3);}return NULL;
}int main(int argc , char *argv[])
{pthread_t pid , cid;int ret ;srand(time(NULL));sem_init(&blank_number , 0 , NUM);sem_init(&stat_number , 0 , 0);ret = pthread_create(&pid , NULL , produser , NULL);if(ret != 0)err_thread(ret , "pthread_create error");ret = pthread_create(&cid , NULL , consumer , NULL);if(ret != 0)err_thread(ret , "pthread_create error");pthread_join(pid , NULL);pthread_join(cid , NULL);sem_destroy(&blank_number);sem_destroy(&stat_number);return 0 ;
}

系統編程完結撒花!開始網絡編程咯~

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

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

相關文章

【Maven】特殊pom.xml配置文件 - BOM

文章目錄 特殊pom.xml配置文件 - BOM一、例子二、注意事項1.特殊的子pom.xml文件2.dependencyManagement 特殊pom.xml配置文件 - BOM 僅用于集中管理項目依賴版本 在 Maven 中&#xff0c;BOM 用于定義一個項目的依賴版本的集合&#xff0c;通常用于管理一組共享的依賴版本。這…

《代碼整潔之道》第5章 格式 - 筆記

你應該選擇一套管理代碼格式的簡單規則。如果是團隊&#xff0c;應該選擇一套團隊一致同意采用的簡單格式規則。 最重要的原則&#xff1a;一致性&#xff08;Consistency&#xff09;&#xff01; 沒有完美的格式規范&#xff0c;但有統一的規范。 整個團隊&#xff08;或者…

C++ 類與對象(中)—— 默認成員函數與運算符重載的深度解析:構造函數,析構函數,拷貝構造函數,賦值運算符重載,普通取地址重載,const取地址重載

在 C 中&#xff0c;類的默認成員函數是編譯器自動生成的重要機制&#xff0c;合理利用這些函數可以簡化代碼編寫&#xff0c;同時避免資源管理錯誤。本文將從構造函數、析構函數、拷貝構造函數、賦值運算符重載等核心內容展開&#xff0c;結合具體案例深入解析。 一、默認成員…

【KWDB創作者計劃】_企業級多模數據庫實戰:用KWDB實現時序+關系數據毫秒級融合(附代碼、性能優化與架構圖)

一、技術背景與行業痛點 1.1 多模數據融合挑戰 場景痛點&#xff1a; 工業物聯網設備每秒產生百萬級傳感器數據&#xff08;時序數據&#xff09;。需關聯設備檔案&#xff08;關系數據&#xff09;生成設備健康報告&#xff0c;傳統方案需多數據庫跳轉&#xff0c;延遲>5…

w~嵌入式C語言~合集4

我自己的原文哦~ https://blog.51cto.com/whaosoft/13870376 一、STM32怎么選型 什么是 STM32 STM32&#xff0c;從字面上來理解&#xff0c;ST是意法半導體&#xff0c;M是Microelectronics的縮寫&#xff0c;32表示32位&#xff0c;合起來理解&#xff0c;STM32就是指S…

Multisim使用教程詳盡版--(2025最新版)

一、Multisim14前言 1.1、主流電路仿真軟件 1. Multisim&#xff1a;NI開發的SPICE標準仿真工具&#xff0c;支持模擬/數字電路混合仿真&#xff0c;內置豐富的元件庫和虛擬儀器&#xff08;示波器、頻譜儀等&#xff09;&#xff0c;適合教學和競賽設計。官網&#xff1a;艾…

分布式理論和事務

微服務和分布式 微服務 是一種軟件架構風格&#xff0c;它將應用程序拆分成一系列小型、獨立的服務&#xff0c;每個服務專注于單一功能&#xff0c;彼此通過輕量級通信機制&#xff08;如 API&#xff09;進行交互。微服務通常是松耦合的&#xff0c;可以獨立開發、部署和擴展…

JAVA:紅黑樹應用的技術指南

&#x1f333; 1、簡述 紅黑樹是一種自平衡二叉查找樹&#xff08;Self-Balancing Binary Search Tree&#xff09;&#xff0c;被廣泛應用于操作系統調度、Java集合、數據庫索引等核心模塊中。本文將從 基本原理 入手&#xff0c;結合 實際應用場景與代碼實例&#xff0c;帶你…

【Pandas】pandas DataFrame rfloordiv

Pandas2.2 DataFrame Binary operator functions 方法描述DataFrame.add(other)用于執行 DataFrame 與另一個對象&#xff08;如 DataFrame、Series 或標量&#xff09;的逐元素加法操作DataFrame.add(other[, axis, level, fill_value])用于執行 DataFrame 與另一個對象&…

【數據可視化-26】基于人口統計與社會經濟數據的多維度可視化分析

?? 博主簡介:曾任某智慧城市類企業算法總監,目前在美國市場的物流公司從事高級算法工程師一職,深耕人工智能領域,精通python數據挖掘、可視化、機器學習等,發表過AI相關的專利并多次在AI類比賽中獲獎。CSDN人工智能領域的優質創作者,提供AI相關的技術咨詢、項目開發和個…

WinForm真入門(18)——DateTimePicker?控件解析

一、基本概念? ?DateTimePicker? 是 Windows 窗體中用于選擇日期和時間的控件&#xff0c;支持以下交互方式&#xff1a; 通過下拉日歷選擇日期通過上下按鈕調整時間直接輸入日期或時間 適用于需要規范日期格式、限制日期范圍或快速輸入的場景&#xff08;如預約系統、數據…

AVFormatContext 再分析

說明 &#xff1a;將 avfromatContext 的變量依次打印分析&#xff0c;根據ffmpeg 給的說明&#xff0c;猜測&#xff0c;結合網上的文章字節寫測試代碼分析。 從常用到不常用依次分析 1. unsigned int nb_streams; 代表 avfromatContext 中 AVStream **streams 的個數 /** …

計算機網絡-運輸層(1)

計算機網絡-運輸層(1) 文章目錄 計算機網絡-運輸層(1)5.1 運輸層概述5.2 運輸層端口號、復用與分用端口號基本概念端口號特性端口號分類重要說明 5.3 UDP與TCP協議對比關鍵區別說明 5.1 運輸層概述 計算機網絡體系結構中的物理層、數據鏈路層以及網絡層共同解決了主機通過異構…

2025 FIC wp

這次比賽計算機和手機大部分題目都比較常規 第一和第四部分有點讓人摸不著頭腦 比賽的時候第一部分有四個題沒出 第四部分基本都沒怎么出 現在復盤一下 把我當時做題的心得和獲取的新知識記錄一下 互聯網取證的部分就先學習一下別的師傅 檢材 鏈接&#xff1a;https://pan.bai…

【大數據技術-聯邦集群RBF】DFSRouter日志一直打印修改Membership為EXPIRED狀態的日志分析

生產環境遇到下面報錯 2025-04-23 17:44:15,780 INFO store.CachedRecordStore (CachedRecordStore.java:overrideExpiredRecords(192)) - Override State Store record MembershipState: router1:8888->hh-fed-sub25:nn2:nn2:8020-EXPIRED 2025-04-23 17:44:15,781 INFO …

【HarmonyOS 5】鴻蒙檢測系統完整性

【HarmonyOS 5】鴻蒙檢測系統完整性 一、前言 從現實安全威脅來看&#xff0c;設備系統完整性風險已影響至移動應用的各個場景。不少用戶因使用越獄設備&#xff08;Jailbreak&#xff09;或非真實設備&#xff08;Emulator&#xff09;&#xff0c;導致應用安全防護機制失效…

學習spark-streaming收獲

1.流處理的核心概念 ?實時 vs微批處理&#xff1a;理解了 Spark Streaming 的微批處理&#xff08;Micro-Batch&#xff09;模型&#xff0c;將流數據切分為小批次&#xff08;如1秒間隔&#xff09;進行處理&#xff0c;與真正的流處理&#xff08;如Flink&#xff09;的區…

Redis一些小記錄

Redis一些小記錄 SpringData Redis&#xff1a;RedisTemplate配置與數據操作 操作String類型數據 String是Redis中最基本的數據類型&#xff0c;可以存儲字符串、整數或浮點數。RedisTemplate提供了ValueOperations接口來操作String類型的數據&#xff0c;支持設置值、獲取值、…

5G融合消息PaaS項目深度解析 - Java架構師面試實戰

5G融合消息PaaS項目深度解析 - Java架構師面試實戰 場景&#xff1a;互聯網大廠Java求職者面試&#xff0c;面試官針對5G融合消息PaaS項目進行提問。 第一輪提問 面試官&#xff1a;馬架構&#xff0c;請簡要介紹5G融合消息PaaS平臺的核心功能和應用場景。 馬架構&#xff…

【C語言極簡自學筆記】C 語言數組詳解:一維數組與二維數組

在 C 語言中&#xff0c;數組是一種非常重要的數據結構&#xff0c;它可以將多個相同類型的元素組織在一起&#xff0c;以便于我們進行批量處理和操作。本文將詳細介紹 C 語言中的一維數組和二維數組&#xff0c;包括它們的定義、初始化、元素訪問以及內存存儲等方面的內容。 …