Linux中多线程使用信号量(信号灯),和sem_wait()函数使用
文章目錄
- 編程環(huán)境:
- 信號(hào)量(信號(hào)燈):
- 使用步驟:
- “生產(chǎn)者-消費(fèi)者”例子:
- 理論模型:
- 代碼分析:
- 代碼實(shí)現(xiàn):
- 運(yùn)行結(jié)果:
- Mac 下對(duì) sem_init()/sem_destory() 不支持:
- 下載地址:
- 系列地址:
簡 述: 上一篇中講解了“條件變量 + 互斥量”的組合使用,演示了 “生產(chǎn)者-消費(fèi)者”模型。本篇講解 互斥量的升級(jí)版:信號(hào)量(信號(hào)燈) 的理解和使用。互斥量與信號(hào)量的關(guān)系,可以簡單理解為 c 和 c++ 的關(guān)系。信號(hào)量的使用的步驟,也是和前面的互斥量很像,不過這次的頭文件改為了 #include <semaphore.h>:
- sem_t sem; //定義變量
- sem_wait(); //加鎖
- …其他代碼
- sem_post(); //解鎖
- sem_destroy(); //銷毀
說明:
本例子是在 Linux 下面運(yùn)行成功的,編譯時(shí)候,時(shí)候需要加參數(shù) -pthread 。
若是想要在 Mac 運(yùn)行改程序,需要改寫替換部分函數(shù)(mac 不支持其中的部分函數(shù))
編程環(huán)境:
💻: uos20 📎 gcc/g++ 8.3 📎 gdb8.0
💻: MacOS 10.14 📎 gcc/g++ 9.2 📎 gdb8.3
信號(hào)量(信號(hào)燈):
簡單理解為里面具有多個(gè)互斥量的集合。是加強(qiáng)版的互斥鎖。
其他:
sem_t sem; int sem_init(sem_t *sem, int pshared, unsigned int value);之間的一些解釋:
sem 變量和 函數(shù) sem_init 中的 sem 參數(shù)是同一個(gè);在函數(shù)里面,第一個(gè)參數(shù) sem 實(shí)際是和第三個(gè)參數(shù) value 關(guān)聯(lián)的;表面上對(duì) sem 進(jìn)行修改,實(shí)際上是修改關(guān)聯(lián)的 value 的值,對(duì)其進(jìn)行 ++ 或 – 操作。(嗯嗯,原理就這么理解就行)。
使用步驟:
sem_t sem;
初始化信號(hào)量
int sem_init(sem_t *sem, int pshared, unsigned int value);- 參數(shù):
- pshared:
- 0 - 線程同步
- 1 - 進(jìn)程同步
- value:
- 最多有幾個(gè)線程操作共享數(shù)據(jù)
- pshared:
加鎖:
//調(diào)用一次相當(dāng)于對(duì) sem 做了一次 -- 操作 int sem_wait(sem_t *sem); //加鎖; => 如果 sem 值為 0,線程會(huì)阻塞int sem_trywait(sem_t *sem); //嘗試加鎖; => sem == 0,加鎖失敗,不阻塞,直接返回int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); //限時(shí)嘗試加鎖解鎖:
int sem_post(sem_t *sem); //相當(dāng)于對(duì)于 sem 做了 ++ 操作銷毀信號(hào)量
int sem_destroy(sem_t *sem);“生產(chǎn)者-消費(fèi)者”例子:
將上一篇的例子改一改,直接使用信號(hào)量來寫這個(gè)例子“生產(chǎn)者-消費(fèi)者”。本篇文章例子如下:
理論模型:
代碼分析:
多看一下理論模型的那個(gè)偽代碼的流程圖,多體會(huì)揣摩其中的箭頭的流向,且一開始這設(shè)置為消費(fèi)者為阻塞,當(dāng)生產(chǎn)者生產(chǎn)之后,為其解鎖。
代碼實(shí)現(xiàn):
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h>sem_t semProducer; sem_t semCustomer;typedef struct node //節(jié)點(diǎn)結(jié)構(gòu) {int data;struct node* next; } Node;Node* g_head = nullptr; //永遠(yuǎn)指向鏈表頭部的指針void* funProducer(void* arg); //生產(chǎn)者--添加一個(gè)頭結(jié)點(diǎn) void* funCustomer(void* arg); //消費(fèi)者--刪除一個(gè)頭結(jié)點(diǎn)int main(int argc, char *argv[]) {pthread_t p1;pthread_t p2;sem_init(&semProducer, 0, 4); //初始化生產(chǎn)者線程信號(hào)量, (賦予 4 個(gè),對(duì)比下一行,讓生產(chǎn)者先運(yùn)行)sem_init(&semCustomer, 0, 0); //初始化消費(fèi)者線程信號(hào)量, (賦予 0 個(gè), 一開始就讓消費(fèi)者處于阻塞狀態(tài))pthread_create(&p1, nullptr, funProducer, nullptr); //創(chuàng)建生產(chǎn)者線程pthread_create(&p2, nullptr, funCustomer, nullptr); //創(chuàng)建消費(fèi)者線程pthread_join(p1, nullptr); //阻塞回收子線程pthread_join(p2, nullptr);sem_destroy(&semProducer); //銷毀生產(chǎn)者信號(hào)量sem_destroy(&semCustomer); //銷毀消費(fèi)者信號(hào)量return 0; }void* funProducer(void* arg) {while (true) {sem_wait(&semProducer); //semProducer--, == 0, 則阻塞Node* pNew = new Node();pNew->data = rand() % 1000; pNew->next = g_head;g_head = pNew;printf("-----funProducer(生產(chǎn)者): %lu, %d\n", pthread_self(), pNew->data);sem_post(&semCustomer); // semCustomer++sleep(rand() % 3); //隨機(jī)休息 0~2 s}return nullptr; }void* funCustomer(void* arg) {while (true) {sem_wait(&semCustomer); //semCustomer--, == 0, 則阻塞Node* pDel = g_head;g_head = g_head->next;printf("-----funCustomer(消費(fèi)者): %lu, %d\n", pthread_self(), pDel->data);delete pDel;sem_post(&semProducer); // semProducer++}return nullptr; }運(yùn)行結(jié)果:
這個(gè)例子是在 uos20 上面運(yùn)行的成功的;編譯的時(shí)候,記得加上參數(shù) -pthread 。
Mac 下對(duì) sem_init()/sem_destory() 不支持:
注意:
MacOS 不支持 sem_init() 和 sem_destroy();這個(gè)例子若是想在 mac 下編譯通過,需要自行修改替換相關(guān)的函數(shù)。
- sem_init(&sem, 0, 1) 改成 sem_open("sem", O_CREAT|O_EXCL, S_IRWXU, 0)
- sem_destory(&sem) 改成 sem_unlink("sem");
- 且支持 pthread_mutex_init(&mutex, NULL) 卻不支持 pthread_mutex_destory(&mutex)
Mac 的該文件在 /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/semaphore.h 路徑,可以查看到該頭文件的源碼,附上詳細(xì) mac 下該庫源碼:
#ifndef _SYS_SEMAPHORE_H_ #define _SYS_SEMAPHORE_H_typedef int sem_t;/* this should go in limits.h> */ #define SEM_VALUE_MAX 32767 #define SEM_FAILED ((sem_t *)-1)#include <sys/cdefs.h>__BEGIN_DECLS int sem_close(sem_t *); int sem_destroy(sem_t *) __deprecated; int sem_getvalue(sem_t * __restrict, int * __restrict) __deprecated; int sem_init(sem_t *, int, unsigned int) __deprecated; sem_t * sem_open(const char *, int, ...); int sem_post(sem_t *); int sem_trywait(sem_t *); int sem_unlink(const char *); int sem_wait(sem_t *) __DARWIN_ALIAS_C(sem_wait); __END_DECLS#endif /* _SYS_SEMAPHORE_H_ */下載地址:
21_semaphore
系列地址:
linuxExample 【21_semaphore】
歡迎 star 和 fork 這個(gè)系列的 Linux / Unix 學(xué)習(xí),附學(xué)習(xí)由淺入深的目錄。
總結(jié)
以上是生活随笔為你收集整理的Linux中多线程使用信号量(信号灯),和sem_wait()函数使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 柳州细胞实验室局部建设规划
- 下一篇: prince2 成功的项目管理_PRIN