1.实验内容(进程的同步)(1) 阅读理解示例程序。
(2) 说明示例程序是否能适合解决 N 个生产者和 1 个消费者问题,并设计实验验证(3) 参照教材修改为 N 个生产者和 1 个消费者问题(4) 思考 N 个生产者和 M 个消费者问题的解决方案(不要求)(5) 利用信号量解决同步问题。
2.实验目的通过程序模拟及验证生产者消费者问题等经典问题,深入理解并发中的同步和互斥的概念3.实验原理(1)进程概念:(1.定义:程序的一次执行过程( 2.三种基本状态 :就绪状态,执行状态,阻塞状态(2)进程同步:(1.定义:并发进程在执行次序上的协调,以达到有效的资源共享和相互合作,使程序执行有可再现性。
( 2.两种形式的制约关系: (一:资源共享关系:进程间接制约,需互斥地访问临界资源。
)、(二:相互合作关系:进程直接制约)(3.临界资源:一次仅允许一个进程访问的资源,引起不可再现性是因为临界资源没有互斥访问。
(3)信号量:定义一个用于表示资源数目的整型量S,它与一般的整型量不同,除初始化外,仅能通过两个标准的原子操作 wait(S)和 signal(S)来访问,俗称 P,V操作。
通俗来讲就是用P 来访问资源后减去一个单位资源,用 V 操作来释放一个单位资源就是现有资源上加一个单位资源。
4.实验内容一:说明示例程序是否能适合解决 N 个生产者和 1 个消费者问题,并设计实验验证答:示例程序不能解决多个生产者和消费者的问题,它是解决单个消费者和生产者的。
如果可以就要修改代码,如“二”所说。
二:多个消费者和生产者的问题生产者 1 如上图所示 :如果要解决多个生产者和消费者的问题:第一步:分析上图得出了两种关系,分别是异步和同步的关系 第二步:异步关系的是生产者和生产者之间的, 因为同一时刻只能有一个生产者 访问缓冲区,所以我们就可以设置临界资源 .获得临界资源的生产者才能把产品 放到缓冲区里第三步:同步关系有两个, 首先是生产者和缓冲区之间, 再是缓冲区和消费者之 间。
他们都满足一前一后的关系, 即当缓冲区空间未满时, 生产者才可以放产品; 缓冲区不为空的时候才可以让消费者取出产品消费。
第四步:设计变量,用 C 语言编程,在 linux 上运行,观察结果。
代码如下:#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <sys/types.h> #include <semaphore.h> int n=10;int buffer[10];// 缓冲区空间大小int in=0,out=0; sem_t mutex,empty,full;// 设置临界资源,定义两个同步关系的信号量 void*producer(void *arg){ // 生产者sem_wait(&mutex); // 访问临界资源int tag= pthread_self()%100;int nextPro;srand(time(NULL)+tag);生产者 6缓冲区 消费者生产者 2生产者 3生产者 4 生产者 5相关头文件while(1){nextPro = rand()%97;sem_wait(&empty); // 生产一个产品,并放入缓冲区,缓冲区空间大小-1 buffer[in] = nextPro;in = (in+1)%n;sem_post(&full);// 生产一个产品,并放入缓冲区,产品数量 +1printf("production:%d\n",nextPro);sem_post(&mutex);// 释放临界资源,给其它线程用 usleep(1000*1000/2);}}void* consumer(void *arg) // 消费者int item;while(1){sem_wait(&full);// 消费一个产品,缓冲区产品数量 -1 item = buffer[out];sem_post(&empty); // 消费一个产品,缓冲区大小 +1printf("consume:%d\n",item);out = (out+1)%n; usleep(1000*1000/2);}}int main(int argc, char const *argv[]){pthread_t tid[6]; sem_init( &mutex, 0,1); sem_init( &empty, 0,10);sem_init( &full, 0,0);for(int i=0;i<6;i++){ pthread_create(&tid[i], NULL, producer, NULL);} // 执行生产者线程 //定义 7 个线程//对临界资源和信号量赋初值//pthread_create(&tid[6], NULL, consumer, NULL);for(int i = 0; i < 6 ; i++)pthread_join(tid[i], NULL);return 0;}三:开启 3个线程,这 3个线程分别输出 A 、B 、C 各 10遍,要求输出结果必须按 ABC 的 顺序显示;如:ABCAB第一步:分析源程序输出结果依次是十个 A ,十个 B ,十个 C ,而我们要求的是输出 A 、B 、C ,A 按顺 序输出,因此可以看出有同步关系, 即一前一后的行为, 存在多种同步机制,就可以引入前 趋图的原理来解决,使源程序输出变成要求所示前驱的概念: 1.要为每一对前驱关系各设置一个同步变量; 2.在“前操作”之后对相应的同步变量执行 V 操作; 3 在“后操作”之前对应的同步变量执行 P 操作第三步:根据上图以及分析,写出修改代码,使代码输出如要求所示#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <sys/types.h>//执行消费者线程#include <semaphore.h>sem_t a,b,c; //设置整型信号量void* p1(void *arg){// 线程输出 A for(inti=0;i<10;i++){ sem_wait(&c);//p - printf("A\n"); sem_post(&a);//v+ //usleep(1);}}void* p2(void *arg){// 线程输出 Bfor(inti=0;i<10;i++){ sem_wait(&a);printf("B\n"); sem_post(&b);//usleep(1);}}void* p3(void *arg){// 线程输出 C for(int i=0;i<10;i++){ sem_wait(&b);//p printf("C\n"); sem_post(&c);//v //usleep(1);}}int main(){sem_init( &a, 0,0);// 设置信号量初值sem_init( &b, 0,0);sem_init( &c, 0,1);// 对信 号量 a 赋初值 pthread_t tid[3];pthread_create(&tid[0], NULL, p1, NULL);pthread_create(&tid[1], NULL, p2, NULL);pthread_create(&tid[2], NULL, p3, NULL); for(int i = 0; i < 3; i++)pthread_join(tid[i], NULL);printf("main is over\n");}5.调试分析1.生产者和消费最开始没理解到什么是同步, 和互斥关系, 用信号量怎么表当把这些概念依次搞懂,然后在看代码就豁然开朗了2.依次输出 A 、B 、C :在做这道题的时候,没理解到前驱关系,以至程序输出结果 不对,或者就是没有输出。
最后仔细思考,发现输出 A 、B 、C 是一个循环,因该给信 号量 a 赋初值 1,才可以解决。
为了使输出程序的结果更稳定,可以在三个线程中加入 睡眠 sleep();增加响应时间。
6.测试预期结果 实际结果当生产者获得临界资源时, 就放入缓冲区,消费者随机 取出一个产品消费,当缓冲 区满了就只有等消费者消 费,或者缓冲区空了就生产 者生产测试数据 pthread_t tid[6]; // 依次创建7 个线程,其中 tid[0]~[5] 为 生产者吗, tid[6] 为消费者, 缓冲区一共有 11 个单位大 小7.问题回答及知识扩展8. 总结蒋贤武(算法设计 +实验报告撰写) :在消费者和生产者、如何依次输出A 、B 、C 这些问题中,应该理解到什么是进程的同步, 和互斥关系,如何有效的设置信号量。
掌握了这些 之后, 对问题的算法设计就容易了。
在具体解决问题中, 首先要分析问题中的同步,异步有 多少种情况,然后在设置相应的信号量来解决问题附件:sy2.1.c sy2.2.csem_t a,b,c;//设置 信号量,实 现前驱同步关系pthread_t tid[3];// 创建线程, 使输出 A 、B 、C 的代码独立运行sleep (); //加入三个线程,使线程有一定的反映时间当代表 A 、B 、C 的线程运行时,只有获得属于自己的信 号量时才能运行,并输出字 符,否则只能等待。