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 ",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 ", 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 ... ", 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. ", 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