今天 ShengYu 來介紹 C/C++ Linux sem_wait 與 sem_post 用法範例,Semaphore 跟 Mutex 相比,雖然 Semaphore 同樣可以用來保護 Critical section,不過它更常被用來確保多執行緒的執行順序。在 Semaphore 中是用 sem_wait 減少與 sem_post 增加 Semaphore 號誌,不像是 Mutex 解鎖還需要同一個人解的 owner ship 特性,也就像解鈴還需繫鈴人概念,以下就來看看 sem_wait 與 sem_post 用法範例吧!
sem_wait() 函式原型:1 int sem_wait (sem_t *sem) ;
解釋:若 semaphore 為非 0,則 semaphore 值減 1;若 semaphore 為 0,則呼叫此 function 的 thread 會被 block ,直到 semaphore 值大於 0。
sem_post() 函式原型:1 int sem_post (sem_t *sem) ;
解釋:對 semaphore 值加 1。
同行程內的 sem_wait() 跟 sem_post() 用法 以下範例是在一個行程內使用 sem_wait() 跟 sem_post(),在 main 函式 會產生 producer 執行緒跟 consumer 執行緒,producer 執行緒負責產生 1-20 之間的亂數並呼叫 insert_item 放入 buffer 中,而 consumer 執行緒負責用 remove_item 來消耗這 中的亂數。
如果 consumer 都沒有消耗任何變數的情況下,在 producer 會因為 sem_wait(&empty)
從初始值 5 一直減 1 直到 0 不能再減的情況下就會被 block 卡住,直到大於 0 為止才會繼續往下執行。
反之,如果 producer 都沒有生產任何變數的情況下,在 consumer 會因為 sem_wait(&full)
從初始值 0 一直減 1 直到 0 不能再減的情況下就會被 block 卡住,直到大於 0 為止才會繼續往下執行。linux-sem_wait.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #define BUFFER_SIZE 5 int buffer[BUFFER_SIZE];pthread_mutex_t mutex;sem_t empty;sem_t full;int insertPointer = 0 , removePointer = 0 ;void *producer (void *param) ;void *consumer (void *param) ;void display_int_array (char *prog, int *arr, int n) { printf ("%s: " , prog); for (int i = 0 ; i < n; i++) { printf ("%d%c" , arr[i], ((i+1 )%16 ) ? ' ' : '\n' ); } printf ("\n" ); } int insert_item (int item) { sem_wait(&empty); pthread_mutex_lock(&mutex); buffer[insertPointer++] = item; insertPointer = insertPointer % BUFFER_SIZE; display_int_array("prod" , buffer, BUFFER_SIZE); pthread_mutex_unlock(&mutex); sem_post(&full); return 0 ; } int remove_item (int *item) { sem_wait(&full); pthread_mutex_lock(&mutex); *item = buffer[removePointer]; buffer[removePointer++] = -1 ; removePointer = removePointer % BUFFER_SIZE; display_int_array("cons" , buffer, BUFFER_SIZE); pthread_mutex_unlock(&mutex); sem_post(&empty); return 0 ; } int main (int argc, char *argv[]) { int sleepTime = 10 , producerThreads, consumerThreads; pthread_mutex_init(&mutex, NULL ); sem_init(&empty, 0 , BUFFER_SIZE); sem_init(&full, 0 , 0 ); srand(time(0 )); { pthread_t tid; pthread_attr_t attr; pthread_attr_init(&attr); pthread_create(&tid, &attr, producer, NULL ); } { pthread_t tid; pthread_attr_t attr; pthread_attr_init(&attr); pthread_create(&tid, &attr, consumer, NULL ); } sleep(sleepTime); return 0 ; } void *producer (void *param) { int random; int r; while (1 ) { r = rand() % 3 ; sleep(r); random = rand() % 20 + 1 ; if (insert_item(random)) fprintf (stderr , "Error" ); printf ("Producer produced %d \n" , random); } } void *consumer (void *param) { int random; int r; while (1 ) { r = rand() % 3 ; sleep(r); if (remove_item(&random)) fprintf (stderr , "Error Consuming" ); else printf ("Consumer consumed %d \n" , random); } }
輸出如下,1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 prod: 7 0 0 0 0 Producer produced 7 cons: -1 0 0 0 0 Consumer consumed 7 prod: -1 14 0 0 0 Producer produced 14 cons: -1 -1 0 0 0 Consumer consumed 14 prod: -1 -1 3 0 0 Producer produced 3 cons: -1 -1 -1 0 0 Consumer consumed 3 prod: -1 -1 -1 8 0 Producer produced 8 cons: -1 -1 -1 -1 0 Consumer consumed 8 prod: -1 -1 -1 -1 10 Producer produced 10 cons: -1 -1 -1 -1 -1 Consumer consumed 10 prod: 10 -1 -1 -1 -1 Producer produced 10
跨行程的 sem_wait() 跟 sem_post() 用法 跨行程的 sem_wait() 跟 sem_post() 用法,用 fork 分離出兩個行程,並在這父行程與子行程兩行程間用共享記憶體傳遞資料,使用 mmap 共享記憶體來當作 mutex,原理跟上述範例類似,只是這是跨行程。
shm_open / ftruncate / mmap 跟 sem_open 這兩種方式都可以成功。linux-sem_wait-2.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 #include <semaphore.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <fcntl.h> void display (char *prog, char *bytes, int n) { printf ("%s: " , prog); for (int i = 0 ; i < n; i++) { printf ("%02x%c" , bytes[i], ((i+1 )%16 ) ? ' ' : '\n' ); } printf ("\n" ); } int main (int argc, char **argv) { int fd, i, count = 0 ,nloop = 2 ,zero = 0 , *ptr; sem_t *mutex; int shm; if ((shm = shm_open("myshm" , O_RDWR | O_CREAT, S_IRWXU)) == 0 ) { perror("shm_open" ); exit (1 ); } if (ftruncate(shm, sizeof (sem_t )) < 0 ) { perror("ftruncate" ); exit (1 ); } if ((mutex = mmap(NULL , sizeof (sem_t ), PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0 )) == MAP_FAILED) { perror("mmap" ); exit (1 ); } fd = open("tmp.txt" ,O_RDWR | O_CREAT, S_IRWXU); write(fd, &zero, sizeof (int )); ptr = mmap(NULL , sizeof (int ), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 ); close(fd); if (sem_init(mutex, 1 , 1 ) < 0 ) { perror("semaphore initilization" ); exit (0 ); } if (fork() == 0 ) { for (i = 0 ; i < nloop; i++) { sem_wait(mutex); printf ("child entered crititical section: %d\n" , (*ptr)++); display("child" , (char *)ptr, 16 ); sleep(2 ); printf ("child leaving critical section\n" ); sem_post(mutex); sleep(1 ); } exit (0 ); } for (i = 0 ; i < nloop; i++) { sem_wait(mutex); printf ("parent entered critical section: %d\n" , (*ptr)++); display("parent" , (char *)ptr, 16 ); sleep(2 ); printf ("parent leaving critical section\n" ); sem_post(mutex); sleep(1 ); } return 0 ; }
以上就是 C/C++ Linux sem_wait 與 sem_post 用法範例介紹, 如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚 支持一下!
其它參考 Semaphores on Linux - sem_init() vs sem_open() - Superpatternshttps://blog.superpat.com/semaphores-on-linux-sem_init-vs-sem_open 在這篇當中提到 shm_open / ftruncate / mmap 跟 sem_open 這兩種方式
其它相關文章推薦 如果你想學習 C++ 相關技術,可以參考看看下面的文章,C/C++ 新手入門教學懶人包 C/C++ Linux shared memory 與 mmap 用法範例