实验相关知识
1、信号量与PV操作
信号量:
信号量是一种用于进程或线程同步的经典同步机制,通常用于解决生产者-消费者问题、读者-写者问题等。
信号量是一个计数器,表示可用资源的数量。信号量的值可以是任意非负整数。在多线程或多进程环境中,信号量用于控制对共享资源的访问。
PV 操作:
P(Produce)操作: 也称为 Wait 操作。执行 P 操作时,信号量的值减一,表示占用了一个资源。如果信号量的值为负数,表示资源不足,执行 P 操作的线程或进程将被阻塞,直到有足够的资源。
V(Vaporize)操作: 也称为 Signal 操作。执行 V 操作时,信号量的值加一,表示释放了一个资源。如果有其他线程或进程正在等待资源,那么其中一个将被唤醒。
使用 C 语言的伪代码表示 PV 操作:
// P 操作 P(semaphore) { while (semaphore <= 0) { // 等待资源 } semaphore--; } // V 操作 V(semaphore) { semaphore++; }
在实际应用中,信号量通常有两种类型:
二进制信号量: 取值为0或1,通常用于互斥操作,如实现临界区的互斥访问。
计数信号量: 可以取任意非负整数,用于表示可用资源的数量,比如用于控制资源池中资源的分配。
2、生产者–消费者问题
生产者-消费者问题是一个经典的并发编程问题,涉及到多个线程共享有限缓冲区的情况。其中,生产者线程负责向缓冲区中生产数据,而消费者线程负责从缓冲区中消费数据。需要确保在并发执行的情况下,生产者和消费者之间的操作是正确有序的,避免数据竞争和死锁等问题。
3、信号量类型的声明:Sem__t sem类型
4、信号量函数原型:Sem_init(sem_t *sem,int pshared,unsigned int value):
①sem参数,为信号量地址
②pshared参数,值为0时,用于线程间同步,值为1时,用于进程间同步
③value参数,指定同时访问的线程数
5、信号量操作函数:
int sem_wait(sem_t *sem):调用一次,信号量sem进程减一。当为0时再次进行减一,进行阻塞。 int sem_post(sem__t *sem):调用一次,信号量sem进程加一。当为N时,再次进行加一,进行阻塞。
实验设备与软件环境
安装环境:分为软件环境和硬件环境
硬件环境:内存ddr3 4G及以上的x86架构主机一部
系统环境:windows 、linux或者mac os x
软件环境:运行vmware或者virtualbox
软件环境:Ubuntu操作系统
实验内容
1. 在main函数下创建两个线程,一个线程为生产者线程,一个线程为消费者线程。
#include <pthread.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <semaphore.h> sem_t empty_sem,full_sem; void init_sem(){ sem_init(&empty_sem,0,1); sem_init(&full_sem,0,0); } void P(sem_t* sem){ if(sem_wait(sem)) perror("P operating error"); } void V(sem_t* sem){ if(sem_post(sem)) perror("V operating error"); } static char share_buf[50]; void * produce(void *arg){ char buf[50]={0}; while(1){ printf("Input message>>\n"); fgets(buf,sizeof(buf),stdin); printf("produce item is>>%s",buf); P(&empty_sem); memcpy(share_buf,buf,sizeof(buf)); V(&full_sem); } return NULL; } void * consumer(void *arg){ char buf[50]={0}; while(1){ P(&full_sem); memcpy(buf,share_buf,sizeof(share_buf)); V(&empty_sem); printf("Consumer item is<<%s",buf); } return NULL; } int main(){ pthread_t produce_tid; pthread_t consumer_tid; init_sem(); pthread_create(&produce_tid,NULL,produce,NULL); pthread_create(&consumer_tid,NULL,consumer,NULL); pthread_join(produce_tid,NULL); pthread_join(consumer_tid,NULL); }
2. 通过编程实现单缓冲区的生产者和消费者问题,通过观察实验结果分析信号量机制在同步与互斥中的实现原理。
互斥原理:互斥原理是先设置一个互斥信号量mutex(可自己定义),再初始化信号量,使mutex=1,然后进行P操作(申请资源使用),这时候mutex-1,即mutex=0,对这个资源进行“加锁”,别人无法再使用这个资源,也就是相当于(C语言中for循环的条件判断,满足条件就进行,不满足就跳出),等程序使用资源完毕时,就会进行V操作(释放资源),对资源进行“解锁”,这时候mutex+1,重新使mutex=1,使资源解放出来,等待下一个程序的使用。
3. 定义两个进程,一个进程为生产者,一个进程为消费者,能不能用进程来模拟单缓冲区的生产者和消费者问题?为什么?
可以
1.生产者进程:产生一个数据,当要送入缓冲区的时候,要检查缓冲区是否已满,若未满,则可将数据送入缓冲区,并通知消费者进程,否则等待。
2.对于消费者进程:当它去取数据的时候,要看缓冲区中是否有数据可以去取,若有则取走一个数据,并通知生产者进程,否则等待。
3.缓冲区是一个临界资源,因此,诸进程对缓冲区的操作程序是一个共享临界区,所以这里还有一个互斥问题。
4. 实际案例
题目:
有一只铁笼子,每次只能放入一只动物,猎手向笼子放入老虎,农民向笼子放入猪,动物园等待取笼中的老虎,饭店等待取笼中的猪,试用PV操作写出能同步执行的程序。
思路:
(1)分析:
只有一只铁笼子,而且每次只能放一种动物(这一点就避免了老虎和猪在一起的情况)。所以设置铁笼子的空间资源为cage,它同时也是笼子的互斥使用,初值为1;
猎手放老虎前先看看有无空间(即笼子里面是否为空),若有则占用笼子,放入老虎。接着向动物园发信号(sem_post(&tiger))(V操作)
动物园先看有无老虎,若有则抢笼子,取走老虎后将笼子释放(sem_post(&cage))(V操作)
农民放猪前先看看有无空间,若有则占用笼子,放入猪。接着向饭店发信号(sem_post(&pig))(V操作)
饭店先看有无猪,若有则抢笼子,取走猪后将笼子释放(sem_post(&cage))(V操作)
(2)信号量:
空间资源cage,初值为1;
TIGHT表示笼子中老虎个数,PIG表示笼子中猪个数,初值均为0
代码:
#include<stdio.h> #include<pthread.h> #include<unistd.h> #include<semaphore.h> sem_t cage,pig,tiger; pthread_mutex_t mutex; int PIG=0,TIGHT=0; void * hunter(void * arg) { while (1) { sleep(2); sem_wait(&cage); sem_post(&tiger); pthread_mutex_lock(&mutex); TIGHT++; printf(" 猎人 %ld 放入 1 只老虎到笼子里面,有 %d 只老虎在笼子里面\n", pthread_self(),TIGHT); pthread_mutex_unlock(&mutex); } return NULL; } void * zoo(void * arg) { while (1) { sleep(2); sem_wait(&tiger); pthread_mutex_lock(&mutex); TIGHT--; printf("动物园%ld 取出 1 只老虎到笼子里面,有 %d 只老虎在笼子里面\n", pthread_self(),TIGHT); sem_post(&cage); pthread_mutex_unlock(&mutex); } return NULL; } void * farmer(void * arg) { while (1) { sleep(2); sem_wait(&cage); sem_post(&pig); pthread_mutex_lock(&mutex); PIG++; printf("农民%ld 放入 1 只猪到笼子里面,有 %d 只猪在笼子里面\n", pthread_self(),PIG); pthread_mutex_unlock(&mutex); } return NULL; } void * cook(void * arg) { while (1) { sleep(2); sem_wait(&pig); pthread_mutex_lock(&mutex); PIG--; printf("厨师%ld 取出 1 只猪到笼子里面,有 %d 只猪在笼子里面\n", pthread_self(),PIG); sem_post(&cage); pthread_mutex_unlock(&mutex); } return NULL; } int main() { //初始化互斥量 sem_init(&cage,0,1); sem_init(&tiger,0,0); sem_init(&pig,0,0); pthread_mutex_init(&mutex, NULL); //创建四个线程 pthread_t tid1, tid2, tid3,tid4; pthread_create(&tid1, NULL,hunter, NULL); pthread_create(&tid2, NULL,zoo, NULL); pthread_create(&tid3, NULL,farmer, NULL); pthread_create(&tid4, NULL,cook, NULL); //等待线程执行结束 pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_join(tid3, NULL); pthread_join(tid4, NULL); pthread_exit(NULL); //注销互斥锁 pthread_mutex_destroy(&mutex); return 0; }