日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux-----信号量

發布時間:2023/12/31 linux 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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個線程,生產者生產數據,消費者消費數據即可
1 #include"RunQueue.hpp"2 void* Consumer(void* arg)3 {4 //RunQueue<int>* rq = (RunQueue<int>*)arg;5 RunQueue<Task>* rq = (RunQueue<Task>*)arg;6 while(true)7 {8 int x,y;9 Task t;10 rq->Pop(t);11 std::cout<<"Consumer done:"<<t.x<<"+"<<t.y<<"="<<t.run()<<std::endl; 12 sleep(1);13 }14 }15 void* Product(void* arg)16 {17 //RunQueue<int>* rq = (RunQueue<int>*)arg;18 RunQueue<Task>* rq = (RunQueue<Task>*)arg;19 while(true)20 {21 int x = rand() % 10 + 1;22 int y = rand() % 100 + 1;23 Task t(x,y);24 rq->Push(t);25 std::cout<<"Product done:"<<x<<"+"<<y<<"=?"<<std::endl;26 }27 }28 int main()29 {30 RunQueue<int> *rq = new RunQueue<int>;31 32 pthread_t c,p;33 pthread_create(&c,nullptr,Consumer,rq);34 pthread_create(&c,nullptr,Product,rq);35 36 pthread_join(c,nullptr);37 pthread_join(p,nullptr);38 delete rq;39 return 0;40 }

先讓消費者慢,生產者一下就把任務生產完,消費者開始做任務

這次讓消費者快,生產者慢


剛開始沒有數據,消費者剛進入申請數據資源失敗掛起等待,生產者申請空間成功,生產者生產一個任務,消費者消費一個任務。
如果想要是多生產者和多消費者參考我上一篇生產者和消費者即可,這里博主就不加了。

總結

以上是生活随笔為你收集整理的Linux-----信号量的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。