1. 程式人生 > >Linux 執行緒】執行緒同步《四》

Linux 執行緒】執行緒同步《四》

1、訊號量

(1)概念

訊號量和互斥鎖(mutex)的區別:互斥鎖只允許一個執行緒進入臨界區,而訊號量允許多個執行緒同時進入臨界區

不多做解釋,要使用訊號量同步,需要包含標頭檔案semaphore.h。

主要用到的函式:

  • int sem_init(sem_t *sem, int pshared, unsigned int value);其中sem是要初始化的訊號量,pshared表示此訊號量是在程序間共享還是執行緒間共享,value是訊號量的初始值。
  • int sem_destroy(sem_t *sem);其中sem是要銷燬的訊號量。只有用sem_init初始化的訊號量才能用sem_destroy
    銷燬。
  • int sem_wait(sem_t *sem);等待訊號量,如果訊號量的值大於0,將訊號量的值減1,立即返回。如果訊號量的值為0,則執行緒阻塞。相當於P操作。成功返回0,失敗返回-1。
  • int sem_post(sem_t *sem); 釋放訊號量,讓訊號量的值加1。相當於V操作。

(2)舉例

《舉例1》

 1 /*************************************************************************
 2     > File Name: semTest1.c
 3     > Summary:  訊號量實現生產者&消費者模型    
4 > Author: xuelisheng 5 > Created Time: 2018年12月18日 6 ************************************************************************/ 7 8 #include <stdio.h> 9 #include <unistd.h> 10 #include <pthread.h> 11 #include <stdio.h> 12 #include <semaphore.h> 13 14
#define NUM 5 15 16 int queue[NUM]; // 全域性陣列實現環形佇列 17 sem_t blank_number, product_number; // 定義2個訊號量:空格子訊號量 產品訊號量 18 19 void *producer(void *arg) 20 { 21 int i = 0; 22 while(1) 23 { 24 // int sem_post(sem_t *sem); 釋放訊號量,讓訊號量的值加1。相當於V操作。 25 sem_wait(&blank_number); // 生產者將空格字數 -- ,為0則阻塞等待 26 queue[i] = rand() % 1000 + 1; // 生產一個產品 27 printf("-----producer-----%d i = %d\n",queue[i], i); 28 sem_post(&product_number); // 將產品數 ++ 喚醒 29 30 i = (i+1) % NUM; // 藉助下標實現環形佇列 31 sleep(rand() % 1); 32 } 33 } 34 35 void *consumer(void *arg) 36 { 37 int i = 0; 38 while(1) 39 { 40 // int sem_wait(sem_t *sem);等待訊號量,如果訊號量的值大於0,將訊號量的值減1,立即返回。如果訊號量的值為0,則執行緒阻塞。相當於P操作。成功返回0,失敗返回-1。 41 sem_wait(&product_number); // 消費者將產品訊號量數 --,為0則阻塞等待 42 printf("-----consumer-----%d i = %d\n", queue[i], i); 43 queue[i] = 0; // 消費一個產品(填充0表示) 44 sem_post(&blank_number); // 消費掉之後,將格子數 ++ 45 46 i= (i+1) % NUM; 47 sleep(rand() % 3); 48 } 49 } 50 51 int main() 52 { 53 pthread_t pid, cid; 54 55 // int sem_init(sem_t *sem, int pshared, unsigned int value);,其中sem是要初始化的訊號量,pshared表示此訊號量是在程序間共享還是執行緒間共享(0表示執行緒),value是訊號量的初始值。 56 sem_init(&blank_number, 0, NUM); 57 sem_init(&product_number, 0, 0); 58 59 pthread_create(&pid, NULL, producer, NULL); 60 pthread_create(&cid, NULL, consumer, NULL); 61 62 pthread_join(pid, NULL); 63 pthread_join(cid, NULL); 64 65 // int sem_destroy(sem_t *sem); 其中sem是要銷燬的訊號量。只有用sem_init初始化的訊號量才能用sem_destroy銷燬。 66 sem_destroy(&blank_number); 67 sem_destroy(&product_number); 68 69 return 0; 70 }

執行結果:

-----producer-----384          i = 0
-----consumer-----384         i = 0
-----producer-----778          i = 1
-----producer-----336          i = 2
-----producer-----493          i = 3
-----producer-----422          i = 4
-----producer-----28            i = 0
-----consumer-----778         i = 1
-----producer-----764          i = 1
-----consumer-----336         i = 2
-----producer-----427          i = 2
-----consumer-----493         i = 3
-----producer-----212          i = 3
-----consumer-----422         i = 4
-----producer-----430          i = 4
-----consumer-----28         i = 0
-----producer-----863          i = 0

 

《舉例2》

 1 /*************************************************************************
 2     > File Name: semTest2.c
 3     > Summary:  基於訊號量的多執行緒同步,作業系統原理中的P,V操作
 4     > Author: xuelisheng 
 5     > Created Time: 2018年12月18日
 6  ************************************************************************/
 7 #include <pthread.h>
 8 #include <semaphore.h>
 9 #include <unistd.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 
13 
14 /* @Scene: 某行業營業廳同時只能服務兩個顧客。
15  * 有多個顧客到來,每個顧客如果發現服務視窗已滿,就等待,
16  * 如果有可用的服務視窗,就接受服務。 */
17 
18 /* 將訊號量定義為全域性變數,方便多個執行緒共享 */
19 sem_t sem;
20 
21 /* 每個執行緒要執行的例程 */
22 void * get_service(void *thread_id)
23 {
24     /* 注意:立即儲存thread_id的值,因為thread_id是對主執行緒中迴圈變數i的引用,它可能馬上被修改 */
25     int customer_id = *((int *)thread_id);
26 
27     if(sem_wait(&sem) == 0)                         // 對共享資源的控制(每次只能2個執行緒對共享資源進行訪問)
28     {
29         usleep(100);                /* service time: 100ms */
30         printf("customer %d receive service ...\n", customer_id);
31         sem_post(&sem);
32     }
33 }
34 
35 #define CUSTOMER_NUM 10
36 
37 int main(int argc, char *argv[])
38 {
39     /* 初始化訊號量,初始值為2,表示有兩個顧客可以同時接收服務 */
40     /* @prototype: int sem_init(sem_t *sem, int pshared, unsigned int value); */
41     /* pshared: if pshared == 0, the semaphore is shared among threads of a process
42      * otherwise the semaphore is shared between processes.   */
43     sem_init(&sem, 0, 2);
44 
45     /* 為每個顧客定義一個執行緒id, pthread_t 其實是unsigned long int */
46     // 定義10個執行緒
47     pthread_t customers[CUSTOMER_NUM];
48 
49     int i, ret;
50     /* 為每個顧客生成一個執行緒 */
51     for(i = 0; i < CUSTOMER_NUM; i++)
52     {
53         int customer_id = i;
54         ret = pthread_create(&customers[i], NULL, get_service, &customer_id);
55         if(ret != 0)
56         {
57             perror("pthread_create");
58             exit(1);
59         }
60         else 
61         {
62             printf("Customer %d arrived.\n", i);
63         }
64         usleep(10);
65     }
66 
67     /* 等待所有顧客的執行緒結束 */
68     /* 注意:這地方不能再用i做迴圈變數,因為可能執行緒中正在訪問i的值 */
69     int j;
70     for(j = 0; j < CUSTOMER_NUM; j++)
71      {
72         pthread_join(customers[j], NULL);
73     }
74 
75     /* Only a  semaphore that  has been initialized  by sem_init(3)
76      * should be destroyed using sem_destroy().*/
77     sem_destroy(&sem);
78     return 0;
79 }

執行結果:(結果不唯一)

Customer 0 arrived.
Customer 1 arrived.
customer 0 receive service ...
Customer 2 arrived.
customer 1 receive service ...
Customer 3 arrived.
customer 2 receive service ...
Customer 4 arrived.
customer 3 receive service ...
Customer 5 arrived.
customer 4 receive service ...
Customer 6 arrived.
customer 5 receive service ...
Customer 7 arrived.
customer 6 receive service ...
Customer 8 arrived.
customer 7 receive service ...
Customer 9 arrived.
customer 8 receive service ...
customer 9 receive service ...

 

 

參考:https://www.cnblogs.com/jiqingwu/p/linux_semaphore_example.html