【線程】讀寫鎖

一、概念

一把讀寫鎖具備三種狀態:

  • 讀模式下加鎖狀態(讀鎖)
  • 寫模式下加鎖轉態(寫鎖)
  • 不加鎖狀態

2. 讀寫鎖特性:

  • 讀寫鎖是寫模式加鎖時,解鎖前,所有對該鎖加鎖的線程都會阻塞。
  • 讀寫鎖是讀模式加時,如果線程以讀模式加鎖會成功,如果線程以寫模式加鎖會阻塞
  • 讀寫鎖是讀模式加鎖時,既有試圖以寫模式加鎖的線程,也有試圖以讀模式加鎖的線程,那么讀模式會讀阻塞隨后的讀模式鎖請求,優先滿足寫模式鎖,讀鎖、寫鎖并行阻塞,寫鎖優先級高

讀寫鎖也叫共享_獨占鎖,當讀寫鎖以讀模式鎖住時,它以共享模式鎖住;當它以寫模式鎖住時,它是獨占鎖住的,寫獨占,讀共享


3. 讀寫鎖的特性:

線程A加讀鎖成功, 又來了三個線程, 做讀操作, 可以加鎖成功

  • 讀共享 - 并行處理

線程A加寫鎖成功, 又來了三個線程, 做讀操作, 三個線程阻塞

  • 寫獨占

線程A加讀鎖成功, 又來了B線程加寫鎖阻塞, 又來了C線程加讀鎖阻塞

  • 讀寫不能同時
  • 寫的優先級高

4. 讀寫鎖場景:

線程A加寫鎖成功, 線程B請求讀鎖

  • 線程B阻塞

線程A持有讀鎖, 線程B請求寫鎖

  • 線程B阻塞

線程A擁有讀鎖, 線程B請求讀鎖

  • 線程B加鎖成功

線程A持有讀鎖, 然后線程B請求寫鎖, 然后線程C請求讀鎖

  • B阻塞,c阻塞 - 寫的優先級高
  • A解鎖,B線程加寫鎖成功,C繼續阻塞
  • B解鎖,C加讀鎖成功

線程A持有寫鎖, 然后線程B請求讀鎖, 然后線程C請求寫鎖

  • BC阻塞
  • A解鎖,C加寫鎖成功,B繼續阻塞
  • C解鎖,B加讀鎖成功

二、主要函數

1. 函數原型:

#include <pthread.h>
pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);  初始化讀寫鎖
pthread_rwlock_destroy(pthread_rwlock_t *rwlock);	 銷毀讀寫鎖

2. 函數原型:阻塞版本

#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);  讀鎖
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);  寫鎖
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);  解鎖

?

三、程序清單

1. 測試代碼:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>int counter;   //全局資源
pthread_rwlock_t rwlock;void *th_write(void *arg)
{int t;int i = (int)arg;while (1) {t = counter;usleep(1000);pthread_rwlock_wrlock(&rwlock);printf("=======write %d: %lu: counter = %d ++counter = %d\n", i, pthread_self(), t, ++counter);pthread_rwlock_unlock(&rwlock);usleep(5000);}return NULL;
}void *th_read(void *arg)
{int t;int i = (int)arg;while (1) {pthread_rwlock_rdlock(&rwlock);printf("---------------read %d: %lu: %d\n", i, pthread_self(), counter);pthread_rwlock_unlock(&rwlock);usleep(900);}return NULL;
}int main()
{int i;pthread_t tid[8];pthread_rwlock_init(&rwlock, NULL);for (i = 0; i < 3; ++i)pthread_create(&tid[i], NULL, th_write, (void*)i);for (i = 0; i < 5; ++i)pthread_create(&tid[i + 3], NULL, th_read, (void*)i);for (i = 0; i < 8; ++i)pthread_join(tid[i], NULL);pthread_rwlock_destroy(&rwlock);return 0;
}

輸出結果:

?

互斥鎖實現讀寫鎖
?

#include<pthread.h>pthread_mutex_t rdLock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t wrLock = PTHREAD_MUTEX_INITIALIZER;
int readCnt = 0;//設置讀鎖調用次數的標志位//實現讀鎖(共享鎖)
void rdLock() 
{pthread_mutex_lock(&rdLock);readCnt++;if (readCnt == 1)//有人讀,于是阻塞寫鎖pthread_mutex_lock(&wrLock);pthread_mutex_unlock(&rdLock);
}void rdUnlock() 
{pthread_mutex_lock(&rdLock);readCnt--;if (readCnt == 0)//表示已沒有人在讀,釋放寫鎖,可以寫入了pthread_mutex_unlock(&wrLock);pthread_mutex_unlock(&rdLock);
}void wrLock() 
{pthread_mutex_lock(&wrLcok);
}void wrUnlock() 
{pthread_mutex_unlock(&wrLock);
}

一、實驗項目

問題描述】程序 trainticket 中,有 100 個線程,其中 90 個線程是查余票數量的,只有 10 個線程搶票,每個線程一次買 10 張票。初始狀態下一共有 1000 張票。因此執行完畢后,還會剩下 900 張票。

程序 trainticket 在運行的時候需要傳入參數,即:

  • 參數 0:表示不加任何鎖
  • 參數 1:表示使用讀寫鎖
  • 參數 2:表示使用互斥量
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>struct Ticket 
{int remain;              // 余票數,初始化為 1000pthread_rwlock_t rwlock; // 讀寫鎖pthread_mutex_t mlock;   // 互斥鎖,主要是為了和讀寫鎖進行對比
}ticket;// 通過命令行傳參數來取得這個值,用來控制到底使用哪一種鎖
// 0:不加鎖 1:加讀寫鎖 2:加互斥鎖
int lock = 0;void* query(void* arg)  //查票線程
{int name = (int)arg;sleep(rand() % 5 + 1);if (lock == 1)pthread_rwlock_rdlock(&ticket.rwlock); // 讀模式加鎖else if (lock == 2)pthread_mutex_lock(&ticket.mlock);int remain = ticket.remain;sleep(1);printf("%03d query: %d\n", name, remain);if (lock == 1)pthread_rwlock_unlock(&ticket.rwlock);else if (lock == 2)pthread_mutex_unlock(&ticket.mlock);return NULL;
}void* buy(void* arg)  // 搶票線程
{int name = (int)arg;if (lock == 1)pthread_rwlock_wrlock(&ticket.rwlock); // 寫模式加鎖else if (lock == 2)pthread_mutex_lock(&ticket.mlock);int remain = ticket.remain;remain -= 10; // 一次買 10 張票sleep(1);ticket.remain = remain;printf("%03d buy 10 tickets\n", name);if (lock == 1)pthread_rwlock_unlock(&ticket.rwlock);else if (lock == 2)pthread_mutex_unlock(&ticket.mlock);sleep(rand() % 5 + 2);return NULL;
}int main(int argc, char* argv[]) 
{lock = 0;if (argc >= 2) lock = atoi(argv[1]);int names[100];pthread_t tid[100];int i;for (i = 0; i < 100; ++i) names[i] = i;ticket.remain = 1000;printf("remain ticket = %d\n", ticket.remain);pthread_rwlock_init(&ticket.rwlock, NULL);pthread_mutex_init(&ticket.mlock, NULL);for (i = 0; i < 100; ++i) {if (i % 10 == 0)pthread_create(&tid[i], NULL, buy, (void*)names[i]);elsepthread_create(&tid[i], NULL, query, (void*)names[i]);}for (i = 0; i < 100; ++i) pthread_join(tid[i], NULL);pthread_rwlock_destroy(&ticket.rwlock);pthread_mutex_destroy(&ticket.mlock);printf("remain ticket = %d\n", ticket.remain);return 0;
}

?

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

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

相關文章

不愧是阿里大佬,mysql存儲過程寫法案例

容器化時代來了 虛擬化技術已經走過了三個時代&#xff0c;沒有容器化技術的演進就不會有 Docker 技術的誕生。 虛擬化技術演進 &#xff08;1&#xff09;物理機時代&#xff1a;多個應用程序可能會跑在一臺機器上。 &#xff08;2&#xff09;虛擬機時代&#xff1a;一臺物…

讀寫鎖(二)

一、實驗項目 【問題描述】程序 trainticket 中&#xff0c;有 100 個線程&#xff0c;其中 90 個線程是查余票數量的&#xff0c;只有 10 個線程搶票&#xff0c;每個線程一次買 10 張票。初始狀態下一共有 1000 張票。因此執行完畢后&#xff0c;還會剩下 900 張票。 程序 …

不看絕對血虧!java字符串轉json

一、對Kafka的認識 1.Kafka的基本概念 2.安裝與配置 3.生產與消費 4.服務端參數配置 二、生產者 1.客戶端開發 必要的參數配置消息的發送序列化分區器生產者攔截器 2.原理分析 整體架構元數據的更新 3.重要的生產者參數 三、消費者 1.消費者與消費組 2.客戶端開發 必要的…

【線程】屏障

一、主要函數應用 1. 函數原型&#xff1a;初始化和回收 int pthread_barrier_init(pthread *barrier, const pthread_barrier_t *attr, unsigned int count); int pthread_barrier_destroy(pthread_barrier_t *barrier);分析&#xff1a; 初始化屏障時&#xff0c;可以使用c…

中高級工程師Java開發!java生成隨機數代碼包

微服務是什么 微服務起源于2005年Peter Rodgers博士在云端運算博覽會提出的微Web服務(Micro-Web-Service)&#xff0c;根本思想類似于Unix的管道設計理念。2014年&#xff0c;由Martin Fowler 與 James Lewis共同提出了微服務的概念&#xff0c;定義了微服務架構風格是一種通過…

Java軟件開發面試題!hr和技術經理一起面

springspring5最全知識梳理大綱&#xff08;總&#xff09; spring最全知識梳理大綱&#xff08;總&#xff09; spring5最全知識梳理大綱&#xff08;總&#xff09; spring最全知識梳理大綱&#xff08;分&#xff09; 為什么要使用Spring&#xff1f; Bean spring事務 面向…

XSI IPC

一、查詢IPC的命令 顯示&#xff1a; ipcs -a 顯示所有共享內核對象 ipcs -m 顯示共享內存 m memory ipcs -q 消息隊列 q queue ipcs -s 顯示信號量 s semphore刪除&#xff1a; ipcrm -m ID 刪除共享內存 ipcrm -q ID 刪除共享隊列 ipcrm -s ID 刪除信號量 二…

Java進階之光!javaunicode碼轉字符

01 如何理解高并發&#xff1f; 高并發意味著大流量&#xff0c;需要運用技術手段抵抗流量的沖擊&#xff0c;這些手段好比操作流量&#xff0c;能讓流量更平穩地被系統所處理&#xff0c;帶給用戶更好的體驗。 我們常見的高并發場景有&#xff1a;淘寶的雙11、春運時的搶票、…

Java進階之光!java字符串類型轉換為int

阿里巴巴篇 1.扎實的計算機專業基礎&#xff0c;包括算法和數據結構&#xff0c;操作系統&#xff0c;計算機網絡&#xff0c;計算機體系結構&#xff0c;數據庫等2.具有扎實的Java編程基礎&#xff0c;理解IO、多線程等基礎框架3.熟練使用Linux系統的常用命令及shell有一定了…

Java進階之光!mysql創建用戶并授權建表

正文 MyBatis 的整體架構分為三層&#xff0c; 分別是基礎支持層、核心處理層和接口層&#xff0c;如下圖所示。 基礎支持層 反射模塊 該模塊對 Java 原生的反射進行了良好的封裝&#xff0c;提供了更加簡潔易用的 API &#xff0c;方便上層使調用&#xff0c;并且對反射操作…

System V 信號量

一、System V 信號量 1.信號量linux命令 顯示&#xff1a; ipcs -a 顯示所有共享內核對象 ipcs -s 顯示信號量 s semphore刪除&#xff1a; ipcrm -s ID 刪除信號量 二、主要函數應用 1. 函數原型&#xff1a; #include <sys/sem.h> int semget(key_t key, int …

Java進階之光!mysql安裝包安裝教程

我聽到的一些發聲 你們賺的錢已經可以了&#xff1a; 我一個發小是做土木工程的&#xff0c;上海大學博士&#xff0c;參與很多著名建筑的工程&#xff0c;但是從薪資上看&#xff0c;還不如一些稍微像樣的公司的6年多的高級開發。為什么&#xff1f;這就是行業的紅利&#xf…

Java進階學習資料!dockerjar內存

準備好套路 **①自我介紹&#xff1a;**千萬不能筐瓢&#xff0c;一定要牢記&#xff0c;自然流暢地介紹自己的學習經歷、工作經歷、項目經歷、個人優勢等等&#xff1b; **②抽象概念&#xff1a;**當面試官問你是如何理解多線程的時候&#xff0c;你要知道從定義、來源、實…

管道(二)

無名管道 測試代碼&#xff1a; #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <string.h>#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0) #define TEST_SIZE 68*1024int mai…

Java進階:java字符串定位語句

正文 模塊&#xff08;Module&#xff09;、組件&#xff08;Component&#xff09;、包&#xff08;Package&#xff09;&#xff0c;這些概念對于我們技術同學并不陌生&#xff0c;但并不是所有人都能理解其要義。 深入理解之后&#xff0c;我才發現&#xff0c;其背后的深…

有名管道

一、有名管道 1. 為何提出有名管道的說法&#xff0c;目的是為了克服無名管道的不足之處&#xff1a; 無名管道只能是用于具有親緣關系的進程之間&#xff0c;這就限制了無名管道的使用范圍。有名管道可以使互不相關的兩個進程互相通信&#xff0c;有名管道可以通過路徑名來指…

Java進階:mysql的事務隔離級別面試題

一面&#xff1a; 阿里巴巴面試答案文末可以領取&#xff01; 1. 觸發新生代GC&#xff0c;如果存活對象總量大于survivor區容量&#xff0c;咋辦 2. 如果任務很多&#xff0c;線程池的阻塞隊列會撐爆內存的哪個區域 3. 棧在堆上嗎 4. GC root有哪些 5. 實例變量可以是GC…

有名管道(二)

一、從FIFO中讀取數據&#xff1a; 約定&#xff1a;如果一個進程為了從FIFO中讀取數據而以阻塞的方式打開FIFO&#xff0c;則稱內核為該進程的讀操作設置了阻塞標志。 如果有進程為寫而打開FIIF&#xff08;寫端存在&#xff09;&#xff0c;且當前FIFO內沒有數據&#xff0…

Java面試2021,java數據可視化項目

AOP簡介 AOP (Aspect Oriented Programing) 稱為&#xff1a;面向切面編程&#xff0c;它是一種編程思想。AOP 是 OOP&#xff08;面向對象編程 Object Oriented Programming)的思想延續 AOP采取橫向抽取機制&#xff0c;取代了傳統縱向繼承體系重復性代碼的編寫方式&#xff0…

gcc的使用

一、gcc編譯過程示意圖 分析&#xff1a; hello程序是一個高級&#xff23;語言程序&#xff0c;這種形式容易被人讀懂。為了在系統上運行hello.c程序&#xff0c;每條&#xff23;語句都必須轉化為低級機器指令。然后將這些指令打包成可執行目標文件格式&#xff0c;并以二進…