【linux】信号量与PV操作 (进程和线程的同步) | 您所在的位置:网站首页 › linux实现生产者与消费者报告的区别 › 【linux】信号量与PV操作 (进程和线程的同步) |
目录 1、基本含义 使用PV操作实现进程互斥时应该注意的是: 2、例子:生产者/消费者模型 3.代码实现信号量 信号量分类:进程间信号量、线程间信号量 在计算机操作系统中,PV操作是进程管理中的难点。 1、基本含义什么是信号量?信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。 PV操作的含义:PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下: P(S):①将信号量S的值减1,即S=S-1; ②如果S>=0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。 V(S):①将信号量S的值加1,即S=S+1; ②如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。 利用信号量和PV操作实现进程互斥的一般模型是: 进程P1 进程P2 …… 进程Pn …… …… …… P(S); P(S); P(S); 临界区; 临界区; 临界区; V(S); V(S); V(S); …… …… …… …… 使用PV操作实现进程互斥时应该注意的是:(1)每个程序中用户实现互斥的P、V操作必须成对出现,先做P操作,进临界区,后做V操作,出临界区。若有多个分支,要认真检查其成对性。 (2)P、V操作应分别紧靠临界区的头尾部,临界区的代码应尽可能短,不能有死循环。 (3)互斥信号量的初值一般为1。 利用信号量和PV操作实现进程同步: PV操作是典型的同步机制之一。用一个信号量与一个消息联系起来,当信号量的值为0时,表示期望的消息尚未产生;当信号量的值非0时,表示期望的消息已经存在。用PV操作实现进程同步时,调用P操作测试消息是否到达,调用V操作发送消息。 使用PV操作实现进程同步时应该注意的是: (1)分析进程间的制约关系,确定信号量种类。在保持进程间有正确的同步关系情况下,哪个进程先执行,哪些进程后执行,彼此间通过什么资源(信号量)进行协调,从而明确要设置哪些信号量。 (2)信号量的初值与相应资源的数量有关,也与P、V操作在程序代码中出现的位置有关。 (3)同一信号量的P、V操作要成对出现,但它们分别在不同的进程代码中。 2、例子:生产者/消费者模型(1)一个生产者,一个消费者,公用一个缓冲区。 定义两个同步信号量: S_empty——表示缓冲区空位数量,初值为1。 S_ full ——表示缓冲区占位数量,初值为0。 伪代码: 生产者进程 while(TRUE){ 生产一个产品; P(S_empty); //buff有空的吗? 产品送往Buffer; V(S_full); //buff已满 } 消费者进程 while(True){ P(S_full); //buff有数据? 从Buffer取出一个产品; V(S_empty); //buff有空 消费该产品; }(2)一个生产者,一个消费者,公用n个(单元的)环形缓冲区。 定义两个同步信号量: S_empty——表示缓冲区空位数量,初值为n。 S_full ——表示缓冲区占位数量,初值为0。 设缓冲区的编号为1~n-1,定义两个指针in和out,分别是生产者进程和消费者进程使用的指,指向下一个可用的缓冲区。 伪代码: 生产者进程 while(TRUE){ 生产一个产品; P(S_empty); 产品送往buffer(in); in=(in+1)mod n; V(S_full); } 消费者进程 while(TRUE){ P(S_full); 从buffer(out)中取出产品; out=(out+1)mod n; V(S_empty); 消费该产品; }(3)一组生产者,一组消费者,公用n个(单元的)环形缓冲区 在这个问题中,不仅生产者与消费者之间要同步,而且各个生产者之间、各个消费者之间还必须互斥地访问缓冲区。 定义四个信号量: empty——表示缓冲区空位数量,初值为n。 full——表示缓冲区占位数量,初值为0。 mutex1——生产者之间的互斥信号量,初值为1。 mutex2——消费者之间的互斥信号量,初值为1。 设缓冲区的编号为1~n-1,定义两个指针in和out,分别是生产者进程和消费者进程使用的指针,指向下一个可用的缓冲区。 伪代码: 生产者进程 while(TRUE){ 生产一个产品; P(empty); P(mutex1); 产品送往buffer(in); in=(in+1)mod n; V(mutex1); V(full); } 消费者进程 while(TRUE){ P(full) P(mutex2); 从buffer(out)中取出产品; out=(out+1)mod n; V(mutex2); V(empty); 消费该产品; }需要注意的是无论在生产者进程中还是在消费者进程中,两个P操作的次序不能颠倒。应先执行同步信号量的P操作,然后再执行互斥信号量的P操作,否则可能造成进程死锁。 原文链接:https://blog.csdn.net/lixiangsheng2012/article/details/83421359 代码示例: 当有进程要求使用共享资源时,需要执行以下操作: 1.系统首先要检测该资源的信号量; 2.若该资源的信号量值大于0,则进程可以使用该资源,此时,进程将该资源的信号量值减1; 3.若该资源的信号量值为0,则进程进入休眠状态,直到信号量值大于0时进程被唤醒,访问该资源; 当进程不再使用由一个信号量控制的共享资源时,该信号量值增加1,如果此时有进程处于休眠状态等待此信号量,则该进程会被唤醒。 2.信号量的具体结构 每个信号量集都有一个与其相对应的结构,该结构定义如下: /* Data structure describing a set of semaphores. */ struct semid_ds { struct ipc_perm sem_perm; /* operation permission struct */ struct sem *sem_base; /* ptr to array of semaphores in set */ unsigned short sem_nsems; /* # of semaphores in set */ time_t sem_otime; /* last-semop() time */ time_t sem_ctime; /* last-change time */ }; /* Data structure describing each of semaphores. */ struct sem { unsigned short semval; /* semaphore value, always >= 0 */ pid_t sempid; /* pid for last successful semop(), SETVAL, SETALL */ unsigned short semncnt; /* # processes awaiting semval > curval */ unsigned short semzcnt; /* # processes awaiting semval == 0 */ };信号量集的结构图如下所示: 使用信号量实现生产者消费者模式 #include "stdafx.h" #include #include #include #include #include #include #include #include #include using namespace std; #pragma comment(lib,"pthreadVC2.lib") #define N 5 //消费者或者生产者的数目 #define M 10 //缓冲数目 int productin = 0; //生产者放置产品的位置 int prochaseout = 0; //消费者取产品的位置 int buff[M] = {0}; //缓冲区初始化为0,开始时没有产品。 sem_t empty_sem; // 同步信号量,当满的时候阻止生产者放产品。 sem_t full_sem; //同步信号量,当没有产品的时候阻止消费者消费。 pthread_mutex_t mutex; //互斥信号量,一次只有一个线程访问缓冲区。 int product_id = 0; //生产者id int prochase_id = 0; //消费者id void SignalExit(int signo) { printf("程序退出%d\n",signo); return; } void PrintProduction() { printf("此时的产品队列为::"); for(int i = 0; i < M; i++ ) { printf("%d ",buff[i]); } printf("\n\n"); } //生产者方法 void* Product(void* pramter) { int id = ++product_id; while(1) { Sleep(5000); //毫秒 sem_wait(&empty_sem); //给信号量减1操作 pthread_mutex_lock(&mutex); productin = productin % M; printf("生产者%d在产品队列中放入第%d个产品\n\n",id,productin+1); buff[productin] = 1; PrintProduction(); ++productin; pthread_mutex_unlock(&mutex); //释放互斥量对象 sem_post(&full_sem); //给信号量的值加1操作 } } //消费者方法/// void* Prochase( void* pramter ) { int id = ++prochase_id; while(1) { Sleep(7000); sem_wait(&full_sem); pthread_mutex_lock(&mutex); prochaseout = prochaseout % M; printf("消费者%d从产品队列中取出第%d个产品\n\n",id,prochaseout+1); buff[prochaseout] = 0; PrintProduction(); ++prochaseout; pthread_mutex_unlock(&mutex); sem_post(&empty_sem); } } int main() { cout |
CopyRight 2018-2019 实验室设备网 版权所有 |