操作系统同步算法(3)
操作系统同步算法(三)
上一篇文章中,我们通过增加一个变量作为标识,从而判断出读者和写者所共享的缓冲区是否有内容,从而实现读写交替,而不是一个线程不停的运行,另外一个“饿死”。
实际上,操作系统里出了互斥锁,还提供了信号量来实现并发情况下的同步。
这里我们通过引入信号量(sem_t )类型的变量来解决共享缓冲区判断问题,这里要注意的一个地方是PV操作的位置以及互斥锁的加锁和信号量的PV操作顺序问题。
首先讨论一下PV操作的位置。P在我们的程序中就是sem_post()函数,作用是对信号量进行加1,这里很显然我们应该将P操作放在写者的线程中。同理,读者中的sem_wait()函数代表的就是V操作,作用是对信号量进行减1,很显然应该放在读者线程中。
关于PV操作和加锁的先后顺序。我最开始是先加锁,再进行P或者V,但是运行的时候我发现程序完全阻塞了,没有任何输出,分析了一下原因:如果是读者先加锁,加锁成功,然后进行V操作,这个时候因为信号量初始值为0,所以进入阻塞。我们再来看写者会是什么情况,写者试图加锁,因为读者已经加锁了,所以阻塞。至此,两个线程全部阻塞。。。所以要注意,一定是先P或者V操作,再进行加锁,否则会造成全部阻塞的死锁现象。
上一篇文章中,我们通过增加一个变量作为标识,从而判断出读者和写者所共享的缓冲区是否有内容,从而实现读写交替,而不是一个线程不停的运行,另外一个“饿死”。
实际上,操作系统里出了互斥锁,还提供了信号量来实现并发情况下的同步。
/** **读写互斥问题 **读的时候不能写,写的时候不能读 **读写交替(使用信号量) **/ #include<stdio.h> #include<pthread.h> #include<semaphore.h> //声明一把互斥锁 pthread_mutex_t mutex; sem_t sem; void writer(void) { while(1) { sem_post(&sem);//V操作,信号量+1 pthread_mutex_lock(&mutex);//加锁 printf("writer加锁成功,开始写...\n"); sleep(5); printf("writer写操作结束,释放互斥锁\n"); pthread_mutex_unlock(&mutex);//释放互斥锁 } } void reader(void) { while(1) { sem_wait(&sem);//P操作,信号量-1 pthread_mutex_lock(&mutex);//加锁 printf("reader加锁成功,开始读...\n"); sleep(5); printf("reader读操作结束,释放互斥锁\n"); pthread_mutex_unlock(&mutex);//释放互斥锁 } } int main(void) { printf("初始化互斥锁\n"); pthread_mutex_init(&mutex,NULL); printf("初始化信号量\n"); sem_init(&sem,0,0); pthread_t thread_writer; pthread_t thread_reader; printf("开始读写线程\n"); pthread_create(&thread_reader,NULL,(void *)reader,NULL); pthread_create(&thread_writer,NULL,(void *)writer,NULL); printf("回收线程\n"); pthread_join(thread_reader,NULL); pthread_join(thread_writer,NULL); printf("运行结束\n"); return 0; }
这里我们通过引入信号量(sem_t )类型的变量来解决共享缓冲区判断问题,这里要注意的一个地方是PV操作的位置以及互斥锁的加锁和信号量的PV操作顺序问题。
首先讨论一下PV操作的位置。P在我们的程序中就是sem_post()函数,作用是对信号量进行加1,这里很显然我们应该将P操作放在写者的线程中。同理,读者中的sem_wait()函数代表的就是V操作,作用是对信号量进行减1,很显然应该放在读者线程中。
关于PV操作和加锁的先后顺序。我最开始是先加锁,再进行P或者V,但是运行的时候我发现程序完全阻塞了,没有任何输出,分析了一下原因:如果是读者先加锁,加锁成功,然后进行V操作,这个时候因为信号量初始值为0,所以进入阻塞。我们再来看写者会是什么情况,写者试图加锁,因为读者已经加锁了,所以阻塞。至此,两个线程全部阻塞。。。所以要注意,一定是先P或者V操作,再进行加锁,否则会造成全部阻塞的死锁现象。