Linux-----信号量
信號量
- 信號量原理
- 信號量概念
- 信號量函數
- 基于環形隊列的生產消費模型
- 空間和數據資源
- 生產者和消費者申請、釋放信號量
- 模擬實現基于環形隊列的生產者消費者模型
信號量原理
- 之前我們知道被多個執行流同時訪問的公共資源叫做臨界資源,而臨界資源不保護的話會造成數據不一性的問題。
- 之前我們用互斥鎖保護臨界資源是把這個臨界資源當做一個整體,只能讓1個執行流訪問臨界資源。現在我們把臨界資源分割成多個區域,當多個執行流訪問不同的區域,此時不會出現數據不一性的問題了。
信號量概念
信號量本質就是一個計數器,描述臨界資源有效個數的計數器。
每個執行流先申請信號量,申請到信號量后同時訪問臨界資源,訪問完后釋放信號量。
信號量的PV操作:
P操作:我們將申請信號量的操作叫做P操作,申請信號量的本質就是有權限訪問臨界資源,申請成功后,P操作的本質就是讓計數器–即可
V操作:將釋放信號量叫做V操作,歸還臨界資源,V操作的本質是讓計數器++
PV操作必須是原子的
執行流要申請信號量要先看到信號量,所以信號量本身就是臨界資源。信號量是保護臨界資源的,我們不能再用信號量去保護信號量,所以信號量的操作必須是原子的
申請信號量失敗掛起等待
當執行流申請信號量時,可能此時信號量為0,說明信號量描述的臨界資源被申請完了,那么這個執行流就要掛起等待,在信號量等待隊列中等待,直到有信號量釋放被喚醒
為什么使用信號量?
這樣可以把臨界資源分成多分,多執行流并行執行,提高了效率。
如何使用信號量呢?下面來看看信號量的一些函數
信號量函數
初始化信號量
int sem_init(sem_t *sem, int pshared, unsigned int value);參數說明:
pshared:0表示線程間共享,非零表示進程間共享
value:信號量初始值
銷毀信號量
int sem_destroy(sem_t *sem);等待信號量
int sem_wait(sem_t *sem); //P()等待信號量,會將信號量的值-1
釋放信號量
int sem_post(sem_t *sem);//V()釋放信號量,表示資源使用完畢,可以歸還資源了。將信號量值加1。
基于環形隊列的生產消費模型
空間和數據資源
生產者關注的是空間資源,消費者關心的是數據資源
- 只要環形隊列中有空間,生產者就可以進行生產
- 而環形隊列中有數據,消費者就可以消費數據
我們不防設空間資源為block_sem,數據資源為data_sem,那么它們的初始值怎么設置呢?
現在是用信號量來描述隊列中的空間和數據資源,剛開始隊列中是沒有數據的,所以給block_sem的初始值設為隊列的空間,data_sem的初始值是0,因為剛開始隊列為空沒有數據的。
生產者和消費者申請、釋放信號量
生產者申請空間資源,釋放數據資源
生產者的操作步驟:
1.如果block_sem不為0,說明隊列中有空間資源,生產者申請信號量成功,那么對應的操作就是P(block_sem),V(data_sem)。此時隊列中多了1塊空間,那么data_sem就要–,也就是V(data_sem)
2.如果block_sem為0,那么生產者申請信號量失敗,此時生產者就要掛起等待,等待有新的空間資源
消費者申請數據資源,釋放空間資源
消費者的操作步驟和生產者基本一樣
1.消費者申請data_sem,若data_sem不為0,消費者申請信號量成功,對應的操作時P(data_sem),那么V(block_sem),釋放的就是空間資源,因為數據占了相應的空間
2.若data_sem,消費者申請信號量失敗,消費者掛起等待,等待新的數據資源。
生產者和消費者要遵守的規則
1.快的不能把慢的套1個圈
2.慢的不能超過快的
- 若生產者的速度比消費者的快,當生產者把隊列生產滿了,并再次遇到消費者。此時生產者再繼續往前生產,那么再生產的數據就會覆蓋掉,此時生產者就要掛起等待
- 同樣的道理,消費者的速度快,當消費者把數據都消費完了再進行消費就會消費到垃圾數據,此時應該掛起等待生產者繼續生產數據
模擬實現基于環形隊列的生產者消費者模型
我們用STL中的vector來模擬環形隊列,分為RunQueue.hpp和mian.cc
相關說明:
RunQueue.hpp
- 我們需要2個下標來標識生產者和消費者的位置,需要生產者消費者申請的資源,block_sem和data_sem
- 我們要提供2個接口,分別是入隊列和出隊列,生產者的P操作,P(block_sem),V(data_sem),消費者的P(data_sem),V(data_sem)
- 每當生產者生產1個空間,對應的生產者下標++,還要模上隊列的空間,以防越界,消費者也是相同的操作
當2個下標指向相同的位置時,要么是剛開始為空,要么是其中1個速度快。當為空時,一定有1個申請信號量失敗,同樣當1個要超過令個是再申請信號量也會失敗。因為信號量的本身就是1個計數器。
1 #pragma once 2 3 #include<iostream>4 #include<pthread.h>5 #include<semaphore.h>6 #include<vector>7 #include<unistd.h>8 class Task9 {10 public:11 int x;12 int y;13 public:14 Task(int _x = 1,int _y = 10)15 16 :x(_x)17 ,y(_y)18 {}19 int run() 20 {21 return x+y;22 }23 ~Task(){}24 };25 template<class T>26 class RunQueue27 {28 private:29 std::vector<T> v;30 int c_index;31 int p_index;32 int cap;33 sem_t block_sem;34 sem_t data_sem;35 public:36 RunQueue(int _cap = 6)37 :cap(_cap)38 ,c_index(0)39 ,p_index(0)40 {41 sem_init(&block_sem,0,cap);42 sem_init(&data_sem,0,0);43 v.resize(10);44 }45 void Push( T& data)46 {47 sem_wait(&block_sem);48 v[p_index] = data;49 p_index++;50 p_index %= cap;51 sem_post(&data_sem);52 }53 void Pop(T& data)54 {55 sem_wait(&data_sem);56 data = v[c_index];57 c_index++;58 c_index %= cap;59 sem_post(&block_sem);60 }61 ~RunQueue()62 {63 sem_destroy(&block_sem);64 sem_destroy(&data_sem);65 }66 67 68 };main.cc
- 主函數的話,創建2個線程,生產者生產數據,消費者消費數據即可
先讓消費者慢,生產者一下就把任務生產完,消費者開始做任務
這次讓消費者快,生產者慢
剛開始沒有數據,消費者剛進入申請數據資源失敗掛起等待,生產者申請空間成功,生產者生產一個任務,消費者消費一個任務。
如果想要是多生產者和多消費者參考我上一篇生產者和消費者即可,這里博主就不加了。
總結
以上是生活随笔為你收集整理的Linux-----信号量的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 德宝会员管理系统服务器密码是多少,易展路
- 下一篇: Linux进程通信之信号量