并發編程--互斥鎖與讀寫鎖

并發編程–互斥鎖與讀寫鎖

文章目錄

  • 并發編程--互斥鎖與讀寫鎖
    • 1. 基本概念
    • 2. 互斥鎖
      • 2.1 基本邏輯
      • 2.2 函數接口
      • 2.3示例代碼1
      • 2.4示例代碼2
    • 3. 讀寫鎖
      • 3.1 基本邏輯
      • 3.2示例代碼

1. 基本概念

互斥與同步是最基本的邏輯概念:

  • 互斥指的是控制兩個進度使之互相排斥,不同時運行。
  • 同步指的是控制兩個進度使之有先有后,次序可控。

img

2. 互斥鎖

2.1 基本邏輯

使得多線程間互斥運行的最簡單辦法,就是增加一個互斥鎖。任何一條線成要開始運行互斥區間的代碼,都必須先獲取互斥鎖,而互斥鎖的本質是一個二值信號量,因此當其中一條線程搶先獲取了互斥鎖之后,其余線程就無法再次獲取了,效果相當于給相關的資源加了把鎖,直到使用者主動解鎖,其余線程方可有機會獲取這把鎖。

img

2.2 函數接口

定義
互斥鎖是一個特殊的變量,定義如下:

#include <pthread>
pthread_mutex_t m;

一般而言,由于互斥鎖需要被多條線程使用,因此一般會將互斥鎖定義為全局變量。

初始化與銷毀
未經初始化的互斥鎖是無法使用的,初始化互斥鎖有兩種辦法:

  • 靜態初始化
  • 動態初始化

靜態初始化很簡單,就是在定義同時賦予其初值:

pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

由于靜態初始化互斥鎖不涉及動態內存,因此無需顯式釋放互斥鎖資源,互斥鎖將會伴隨程序一直存在,直到程序退出為止。而所謂動態初始化指的是使用 pthread_mutex_init() 給互斥鎖分配動態內存并賦予初始值,因此這種情形下的互斥鎖需要在用完之后顯式地進行釋放資源,接口如下:

#include <pthread.h>// 初始化互斥鎖
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);// 銷毀互斥鎖
int pthread_mutex_destroy(pthread_mutex_t *mutex);

接口說明:

  • mutex:互斥鎖
  • attr:互斥鎖屬性(一般設置為NULL)

加鎖與解鎖
互斥鎖的基本操作就是加鎖與解鎖,接口如下:

#include <pthread.h>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;// 加鎖
pthread_mutex_lock( &m );// 解鎖
pthread_mutex_unlock( &m );

2.3示例代碼1

將此前判斷偶數的代碼用互斥鎖加以改進如下:

// concurrency.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <pthread.h>pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;int global = 100;void *isPrime(void *arg)
{while(1){pthread_mutex_lock(&m);// 一段樸素的代碼if(global%2 == 0)printf("%d是偶數\n", global);pthread_mutex_unlock(&m);}
}int main()
{pthread_t tid;pthread_create(&tid, NULL, isPrime, NULL);// 一條人畜無害的賦值語句while(1){pthread_mutex_lock(&m);global = rand() % 5000;pthread_mutex_unlock(&m);}
}

運行結果如下:

gec@ubuntu:~$ ./concurrency
492是偶數
2362是偶數
2778是偶數
3926是偶數
540是偶數
3426是偶數
4172是偶數
112是偶數
368是偶數
2576是偶數
1530是偶數
1530是偶數
2862是偶數
4706是偶數
...
gec@ubuntu:~$ 

可見,有了互斥鎖之后,輸出的結果正確了。

2.4示例代碼2

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>pthread_mutex_t m;void *routine(void *arg)
{char *msg = (char *)arg;#ifdef MUTEXpthread_mutex_lock(&m);#endifwhile(*msg != '\0'){fprintf(stderr,"%c",*msg);usleep(100);msg++;}#ifdef MUTEXpthread_mutex_unlock(&m);#endifpthread_exit(NULL);
}int main(void)
{pthread_mutex_init(&m,NULL);pthread_t t1,t2;pthread_create(&t1,NULL,routine,"AAAAAAAAAAA");pthread_create(&t2,NULL,routine,"BBBBBBBBBBB");pthread_exit(NULL);
}

通過宏定義實現代碼的不同運行,輸出不同的結果。若不使用互斥鎖的話,則直接運行,結果將會是AB交互是輸出,兩個線程t1,t2會同時運行,交互式輸出;若使用互斥鎖的話,會輸出單獨輸出一個線程的結果,然后再輸出另一個線程的結果。

若要使用互斥鎖則如下:

gcc pthread_mutex.c -o pthread_mutex -lpthread -DMUTEX

3. 讀寫鎖

3.1 基本邏輯

對于互斥鎖而言,凡是涉及臨界資源的訪問一律加鎖,這在并發讀操作的場景下會大量浪費時間。要想提高訪問效率,就必須要將對資源的讀寫操作加以區分:讀操作可以多任務并發執行,只有寫操作才進行恰當的互斥。這就是讀寫鎖的設計來源。

img
讀寫鎖提高了資源訪問的效率

定義
與互斥鎖類似,讀寫鎖也是一種特殊的變量:

pthread_rwlock_t rw;

初始化
與互斥鎖類似,讀寫鎖也分成靜態初始化和動態初始化:

#include <pthread.h>// 靜態初始化:
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;// 動態初始化與銷毀:
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

加鎖
讀寫鎖最大的特點是對即將要做的讀寫操作做了區分:

  • 讀操作可以共享,因此多條線程可以對同一個讀寫鎖加多重讀鎖
  • 寫操作天然互斥,因此多條線程只能有一個擁有寫鎖。(注意寫鎖與讀鎖也是互斥的)
#include <pthread.h>// 讀鎖
// 1,阻塞版本
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
// 2,非阻塞版本
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);// 寫鎖
// 1,阻塞版本
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
// 2,非阻塞版本
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

操作原則:

  • 如果只對數據進行讀操作,那么就加 → 讀鎖。
  • 如果要對數據進行寫操作,那么就加 → 寫鎖。

解鎖

#include <pthread.h>int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

3.2示例代碼

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>char global = 'X';pthread_rwlock_t rwlock;void *routine(void *arg)
{#ifdef RDLOCKpthread_rwlock_rdlock(&rwlock);#elif WRLOCKpthread_rwlock_wrlock(&rwlock);#endifint i = 1000;while(i > 0){fprintf(stderr,"[%c:%c]",*(char*)arg,global);i--;}pthread_rwlock_unlock(&rwlock);pthread_exit(NULL);
}int main(void)
{pthread_rwlock_init(&rwlock,NULL);pthread_t t1,t2,t3;pthread_create(&t1,NULL,routine,"1");pthread_create(&t2,NULL,routine,"2");pthread_create(&t3,NULL,routine,"3");pthread_exit(NULL);
}

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

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

相關文章

親手打造可視化故事線管理工具:開發全流程、難點突破與開發過程經驗總結

親手打造可視化故事線管理工具&#xff1a;開發全流程、難點突破與開發過程經驗總結 作為還沒入門的業余編程愛好者&#xff0c;奮戰了2天&#xff0c;借助AI開發一款FLASK小工具&#xff0c;功能還在完善中&#xff08;時間軸可以跟隨關聯圖縮放&#xff0c;加了一個用C鍵控制…

網絡攻防技術-虛擬機安裝和nmap端口掃描

文章是博主上實驗課做的實驗和心得體會&#xff0c;有些高深的地方我可能也比較一知半解&#xff0c;歡迎來交流。全文參考課程所習得&#xff0c;純粹梳理知識點和分享&#xff0c;如有不妥請聯系修改。 文章側重實驗部分&#xff0c;也會講述實驗相關的理論知識。理論后期如果…

中斷的硬件框架

今天呢&#xff0c;我們來講講中斷的硬件框架&#xff0c;這里會去舉3個開發板&#xff0c;去了解中斷的硬件框架&#xff1a; 中斷路徑上的3個部件&#xff1a; 中斷源 中斷源多種多樣&#xff0c;比如GPIO、定時器、UART、DMA等等。 它們都有自己的寄存器&#xff0c;可以…

動手學深度學習:手語視頻在VGG模型中的測試

前言 其他所有部分同上一篇AlexNet一樣&#xff0c;所以就不再贅訴&#xff0c;直接看VGG搭建部分。 模型 VGG是第一個采取塊進行模塊化搭建的模型。 def vgg_block(num_convs,in_channels,out_channels):layers[]for _ in range(num_convs):layers.append(nn.Conv2d(in_ch…

信息學奧賽一本通 1498:Roadblocks | 洛谷 P2865 [USACO06NOV] Roadblocks G

【題目鏈接】 ybt 1498&#xff1a;Roadblocks 洛谷 P2865 [USACO06NOV] Roadblocks G 【題目考點】 1. 圖論&#xff1a;嚴格次短路徑 嚴格次短路的路徑長度必須大于最短路的路徑長度。 非嚴格次短路的路徑長度大于等于最短路的路徑長度。 【解題思路】 每個交叉路口是一…

Arm CPU安全通告:基于TrustZone的Cortex-M系統面臨多重故障注入攻擊

安全之安全(security)博客目錄導讀 目錄 一、概述 二、致謝 三、參考文獻??????Black Hat USA 2022 | Briefings Schedule 四、版本歷史 一、概述 Arm注意到BlackHat 2022大會官網發布的演講摘要《糟糕..&#xff01;我又一次故障注入成功了&#xff01;——如何突…

【頻域分析】包絡分析

【頻域分析】包絡分析 算法配置頁面 可以一鍵導出結果數據 報表自定義繪制 獲取和下載【PHM學習軟件PHM源碼】的方式 獲取方式&#xff1a;Docshttps://jcn362s9p4t8.feishu.cn/wiki/A0NXwPxY3ie1cGkOy08cru6vnvc

ElMessage

以下是關于 ElMessage 的詳細說明和使用方法&#xff1a; 什么是 ElMessage ElMessage 是 Element Plus 提供的一個全局消息提示組件&#xff0c;用于在頁面上顯示短暫的消息提示。它可以用于顯示成功、警告、錯誤等不同類型的消息。 基本用法 1. 引入 ElMessage 在使用 E…

全面解析 KaiwuDB 數據庫的數據類型

在現代數據庫管理系統中&#xff0c;數據類型的選擇至關重要。它不僅決定了數據存儲的效率&#xff0c;還影響到查詢的速度和數據的一致性。KaiwuDB&#xff0c;作為一款開源的分布式數據庫&#xff0c;提供了多種數據類型&#xff0c;以適應不同的業務需求和存儲要求。本文將全…

【計網】網絡交換技術之分組交換(復習自用,重要1)

復習自用的&#xff0c;處理得比較草率&#xff0c;復習的同學或者想看基礎的同學可以看看&#xff0c;大佬的話可以不用浪費時間在我的水文上了 另外兩種交換技術可以直接點擊鏈接訪問相關筆記&#xff1a; 電路交換 報文交換 一、分組交換的定義 1.定義 分組交換&#x…

C++ STL及Python中等效實現

一. STL 概述 STL 包含以下核心組件&#xff1a; 容器&#xff08;Containers&#xff09;&#xff1a;存儲數據的結構&#xff0c;如數組、鏈表、集合等。迭代器&#xff08;Iterators&#xff09;&#xff1a;用于遍歷容器的接口&#xff0c;類似指針。算法&#xff08;Alg…

python-63-前后端分離之圖書管理系統的Flask后端

文章目錄 1 flask后端1.1 數據庫實例extension.py1.2 數據模型models.py1.3 .flaskenv1.4 app.py1.5 運行1.6 測試鏈接2 關鍵函數和文件2.1 請求視圖類MethodView2.2 .flaskenv文件3 參考附錄基于flask形成了圖書管理系統的后端,同時對其中使用到的關鍵文件.flaskenv和函數類M…

藍橋杯真題——好數、R格式

目錄 藍橋杯2024年第十五屆省賽真題-好數 【模擬題】 題目描述 輸入格式 輸出格式 樣例輸入 樣例輸出 提示 代碼1&#xff1a;有兩個案例過不了&#xff0c;超時 藍橋杯2024年第十五屆省賽真題-R 格式 【vector容器的使用】 題目描述 輸入格式 輸出格式 樣例輸入…

Python中NumPy的索引和切片

在數據科學和科學計算領域&#xff0c;NumPy是一個功能強大且廣泛使用的Python庫。它提供了高效的多維數組對象以及豐富的數組操作函數&#xff0c;其中索引和切片是NumPy的核心功能之一。通過靈活運用索引和切片操作&#xff0c;我們可以輕松訪問和操作數組中的元素&#xff0…

設計模式:策略模式 - 消除復雜條件判斷的利器

一、什么是策略模式&#xff1f; 策略模式&#xff08;Strategy Pattern&#xff09;是一種行為型設計模式&#xff0c;它將一組算法或業務邏輯封裝為獨立的策略類&#xff0c;使這些策略可以互換使用&#xff0c;并通過上下文類動態選擇合適的策略。 核心思想 ? 將不同的行…

LeetCode hot 100—不同路徑

題目 一個機器人位于一個 m x n 網格的左上角 &#xff08;起始點在下圖中標記為 “Start” &#xff09;。 機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角&#xff08;在下圖中標記為 “Finish” &#xff09;。 問總共有多少條不同的路徑&#xff1f; …

pytorch查詢字典、列表維度

輸出tensor變量維度 print(a.shape)輸出字典維度 for key, value in output_dict.items():if isinstance(value, torch.Tensor):print(f"{key} shape:", value.shape)輸出列表維度 def get_list_dimensions(lst):# 基線條件&#xff1a;如果lst不是列表&#xff0…

多坐標系變換全解析:從相機到WGS-84的空間坐標系詳解

多坐標系變換全解析:從相機到WGS-84的空間坐標系詳解 一、常見坐標系簡介二、各坐標系的功能和使用場景1. WGS-84 大地坐標系(經緯高)2. 地心直角坐標系(ECEF)3. 本地 ENU / NED 坐標系4. 平臺坐標系(Body)5. 相機坐標系三、坐標變換流程圖四、如何選用合適的坐標系?五…

【NumPy科學計算:高性能數組操作核心指南】

目錄 前言&#xff1a;技術背景與價值當前技術痛點解決方案概述目標讀者說明 一、技術原理剖析核心概念圖解關鍵技術模塊技術選型對比 二、實戰演示環境配置要求核心代碼實現運行結果驗證 三、性能對比測試方法論量化數據對比結果分析 四、最佳實踐推薦方案 ?常見錯誤 ?調試技…

【特權FPGA】之PS/2鍵盤解碼

0 故事背景 見過這種接口的朋友們&#xff0c;大概都已經成家立業了吧。不過今天我們不討論這種接口的歷史&#xff0c;只講講這種接口的設計。&#xff08;如果還沒有成家的朋友也別生氣&#xff0c;做自己想做的事情就對了&#xff01;&#xff09; 1 時序分析 數據幀格式如圖…