(转)信号量同步
参考1:https://www.cnblogs.com/jiqingwu/p/linux_semaphore_example.html linux线程的信号量同步
参考2:https://www.cnblogs.com/fangshenghui/p/4039946.html linux进程间通信-信号量
一 为什么使用信号量
为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问 代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它, 也就是说信号量是用来调协进程对共享资源的访问的。其中共享内存的使用就要用到信号量。
二 信号量的工作原理
由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.
举个例子,就是 两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为 当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。
三 使用信号量
主要用到的函数
1 sem_init(sem_t *sem, int pshared, unsigned int value) 2 /*sem 要初始化的信号量 3 pshared 在进程间(pshared != 0)还是线程间共享(pshared = 0) 4 value 信号量的初始值 */ 5 6 sem_wait(sem_t *sem) 7 /*等待信号量,值大于1,则将信号量的值减1,立即返回 8 值为0,则线程阻塞 9 成功返回0,失败返回-1 10 */ 11 sem_post(sem_t *sem) 12 /*释放信号量,让信号量的值加1 */ 13 14 sem_destroy(sem_t *sem) 15 /*销毁信号量*/
实例:模拟窗口服务系统
1 /* @purpose: 基于信号量的多线程同步,操作系统原理中的P,V操作 2 * @author: jollywing@foxmail.com 3 * @create: 2015-03-20 Fri 4 * */ 5 6 #include <pthread.h> 7 #include <semaphore.h> 8 #include <unistd.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 12 13 /* @Scene: 某行业营业厅同时只能服务两个顾客。 14 * 有多个顾客到来,每个顾客如果发现服务窗口已满,就等待, 15 * 如果有可用的服务窗口,就接受服务。 */ 16 17 /* 将信号量定义为全局变量,方便多个线程共享 */ 18 sem_t sem; 19 20 /* 每个线程要运行的例程 */ 21 void * get_service(void *thread_id) 22 { 23 /* 注意:立即保存thread_id的值,因为thread_id是对主线程中循环变量i的引用,它可能马上被修改 */ 24 int customer_id = *((int *)thread_id); 25 26 if(sem_wait(&sem) == 0) { 27 usleep(100); /* service time: 100ms */ 28 printf("customer %d receive service ...\n", customer_id); 29 sem_post(&sem); 30 } 31 } 32 33 #define CUSTOMER_NUM 10 34 35 int main(int argc, char *argv[]) 36 { 37 /* 初始化信号量,初始值为2,表示有两个顾客可以同时接收服务 */ 38 /* @prototype: int sem_init(sem_t *sem, int pshared, unsigned int value); */ 39 /* pshared: if pshared == 0, the semaphore is shared among threads of a process 40 * otherwise the semaphore is shared between processes. */ 41 sem_init(&sem, 0, 2); 42 43 /* 为每个顾客定义一个线程id, pthread_t 其实是unsigned long int */ 44 pthread_t customers[CUSTOMER_NUM]; 45 46 int i, ret; 47 /* 为每个顾客生成一个线程 */ 48 for(i = 0; i < CUSTOMER_NUM; i++){ 49 int customer_id = i; 50 ret = pthread_create(&customers[i], NULL, get_service, &customer_id); 51 if(ret != 0){ 52 perror("pthread_create"); 53 exit(1); 54 } 55 else { 56 printf("Customer %d arrived.\n", i); 57 } 58 usleep(10); 59 } 60 61 /* 等待所有顾客的线程结束 */ 62 /* 注意:这地方不能再用i做循环变量,因为可能线程中正在访问i的值 */ 63 int j; 64 for(j = 0; j < CUSTOMER_NUM; j++) { 65 pthread_join(customers[j], NULL); 66 } 67 68 /* Only a semaphore that has been initialized by sem_init(3) 69 * should be destroyed using sem_destroy().*/ 70 sem_destroy(&sem); 71 return 0; 72 }
编译:
1 gcc signal_test.c -o signal_test -lpthread
运行结果(每次不同)
1 customer 0 arrived. 2 customer 1 arrived. 3 customer_id 0 is in service. 4 customer 2 arrived. 5 customer_id 1 is in service. 6 customer 3 arrived. 7 customer_id 2 is in service. 8 customer 4 arrived. 9 customer_id 3 is in service. 10 customer 5 arrived. 11 customer_id 4 is in service. 12 customer 6 arrived. 13 customer 7 arrived. 14 customer_id 5 is in service. 15 customer_id 6 is in service. 16 customer 8 arrived. 17 customer_id 7 is in service. 18 customer 9 arrived. 19 customer_id 8 is in service. 20 customer_id 9 is in service.