[设计目的]
1. 学习并实现进程同步机制;
2. 理解互斥(Mutex)和信号量(Semaphore)的使用*;
3. 掌握解决并发访问问题的方法。
[设计要求]
在操作系统中,需要设计一个算法来控制对共享资源的访问,确保数据的一致性和完整性。
在操作系统中,需要考虑效率和公平性,避免饥饿和死锁。
设计思路:
读者-写者问题是一个非常经典的问题,基本上每个学校老师在讲课操作系统这门课时都会详细讲解,这里我就不赘述了,但是我们要理解其中的三个原则:“读读共享,读写互斥,写写互斥”
然后还有三种情况,读者优先,写者优先,读者写者公平竞争
[设计思想及内容]
设计思想通常基于使用互斥锁来保护资源,以及使用信号量来控制读者和写者的数量。可以采用以下策略:
1使用一个互斥锁来保护资源的读写操作。
2使用两个信号量,一个用于控制读者的数量(读者信号量),另一个用于控制写者(写者信号量)。
[数据结构设计]
sem_t wmutex: 写者互斥信号量,用于保证写者在写入时的互斥访问。
sem_t rmutex: 读者互斥信号量,用于同步读者对共享资源的访问。
sem_t mutex1: 用于保护共享资源,确保一次只有一个读者或写者可以访问资源(在写者优先和公平竞争的代码中使用)。
sem_t num: 用于控制对共享资源的访问,特别是实现写者优先策略(在写者优先和公平竞争的代码中使用)。
pthread_t writer, pthread_t reader[N]: 存储创建的线程标识符,用于后续的线程同步操作。
信号量 sem_t 被用作互斥锁的功能来保护共享资源。
信号量的 sem_wait 和 sem_post 操作隐含地实现了条件变量的功能,用于线程间的同步。
[程序源码]
readfirst 读者优先
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
//用于记录当前正在读取的读者数量
#define N 5
//用于记录当前正在读取的读者数量
int count=0;
//控制读者和写者访问次数
int a = 5;
int b = 5;
int r[N]={0,1,2,3,4};
sem_t wmutex;// 写者互斥信号量
sem_t rmutex;//读者互斥信号量
//产生延时函数
void delay()
{
sleep(1);
}
void Reader(void *arg)
{
int i=*(int *)arg;
while(a>0)
{
a--;
delay();
//等待读者互斥信号量,确保对count的原子操作
sem_wait(&rmutex);
if(count==0)//如果没有其他读者,等待写者互斥信号量
sem_wait(&wmutex);
count++;//增加读者数量
sem_post(&rmutex);
printf("Reader%d is reading!\n",i);
printf("Reader%d reads end!\n",i);
//再次等待读者互斥信号量,准备减少读者数量
sem_wait(&rmutex);
count--;//减少读者数量
if(count==0)//如果没有其他读者,释放写者互斥信号量
sem_post(&wmutex);
sem_post(&rmutex);
}
}
void Writer()
{
while(b>0)
{
b--;
delay();
sem_wait(&wmutex);//等待写者互斥信号量
printf("writer is writing!\n");
printf("writer writes end!\n");
sem_post(&wmutex);//释放写者互斥信号量
}
}
int main()
{
int i;
pthread_t writer,reader[N];
//初始化互斥信号量
sem_init(&wmutex,0,1);//互斥锁初始化
sem_init(&rmutex,0,1);
for(i=0;i<5;i++)//创建读者线程
{
pthread_create(&reader[i],NULL,(void *)Reader,&r[i]);
}
//创建写者进程
pthread_create(&writer,NULL,(void *)Writer,NULL);
//等待写者进程结束
pthread_join(writer,NULL);//线程等待
//销毁互斥信号量
sem_destroy(&rmutex); //互斥锁的销毁
sem_destroy(&wmutex);
return 0;
}
Writefirst 写者优先
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_READERS 5
#define MAX_WRITERS 2
// 用于记录当前正在读取的读者数量
int reader_count = 0;
// 用于记录是否有写者正在等待
int writer_waiting = 0;
// 用于记录是否有写者正在写入
int writer_active = 0;
// 函数声明
void lock();
void unlock();
void writer_lock();
void writer_unlock();
// 信号量定义
sem_t mutex;
sem_t readers;
sem_t writers;
sem_t write_resource;
int main() {
pthread_t readers[MAX_READERS], writers[MAX_WRITERS];
// 初始化信号量
sem_init(&mutex, 0, 1);
// 互斥锁,用于保护共享资源
sem_init(&readers, 0, MAX_READERS);
// 读者信号量,最大读者数为MAX_READERS
sem_init(&writers, 0, MAX_WRITERS);
// 写者信号量,最大写者数为MAX_WRITERS
sem_init(&write_resource, 0, 1);
// 写者资源锁,确保一次只有一个写者访问资源
// 创建读者线程
for (int i = 0; i < MAX_READERS; ++i) {
pthread_create(&readers[i], NULL, (void *(*)(void *))reader, NULL);
}
// 创建写者线程
for (int i = 0; i < MAX_WRITERS; ++i) {
pthread_create(&writers[i], NULL, (void *(*)(void *))writer, NULL);
}
// 等待线程结束
for (int i = 0; i < MAX_READERS; ++i) {
pthread_join(readers[i], NULL);
}
for (int i = 0; i < MAX_WRITERS; ++i) {
pthread_join(writers[i], NULL);
}
// 销毁信号量
sem_destroy(&mutex);
sem_destroy(&readers);
sem_destroy(&writers);
sem_destroy(&write_resource);
return 0;
}
// 读者线程函数
void* reader(void *args) {
while (1) {
// 读者进入
sem_wait(&mutex);
reader_count++;
if (reader_count == 1) {
// 如果是第一个读者,需要获取读资源锁
sem_wait(&readers);
}
sem_post(&mutex);
// 模拟读取操作
printf("Reader is reading.\n");
sleep(1);
// 读者退出
sem_wait(&mutex);
reader_count--;
if (reader_count == 0) {
// 如果是最后一个读者,需要释放读资源锁并检查是否有写者等待
sem_post(&readers);
if (writer_waiting) {
writer_lock();
writer_active = 1;
writer_unlock();
}
}
sem_post(&mutex);
}
return NULL;
}
// 写者线程函数
void* writer(void *args) {
while (1) {
// 写者进入等待区
writer_lock();
writer_waiting = 1;
// 如果写者正在写入或有其他写者在等待,则等待
while (writer_active || reader_count > 0) {
sem_wait(&write_resource);
}
// 写者获得资源,开始写入
writer_active = 1;
writer_waiting = 0;
writer_unlock();
// 模拟写入操作
printf("Writer is writing.\n");
sleep(2);
// 写者完成写入,释放资源
writer_lock();
writer_active = 0;
if (writer_waiting) {
sem_post(&write_resource);
}
writer_unlock();
}
return NULL;
}
// 锁定函数,用于读者和写者的互斥操作
void lock() {
sem_wait(&mutex);
}
// 解锁函数
void unlock() {
sem_post(&mutex);
}
// 写者锁定函数,用于写者之间的互斥和等待
void writer_lock() {
sem_wait(&writers);
}
// 写者解锁函数
void writer_unlock() {
sem_post(&writers);
}
Readwrite 读者写者公平竞争
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#define N 5
int readcount=0,writecount=0,a=5,b=2;
int r[N]={0,1,2,3,4};
int w[N]={0,1};
sem_t wmutex; // 写者互斥信号量,用于控制写者之间的互斥
sem_t rmutex; // 读者互斥信号量,用于控制读者之间的互斥
// 共享资源互斥信号量,用于控制读者和写者对共享资源的互斥
sem_t mutex1;
sem_t num; // 用于控制对共享资源的访问,实现写者优先
void delay()
{
sleep(1);
}
//读者线程函数
void Reader(void *arg)
{
int i=*(int *)arg;
while(a>0)
{
a--;//减少读者访问次数
delay();//延迟
//等待读者互斥信号量,确保对count的原子操作
sem_wait(&num);//在无写者进程时进入
sem_wait(&rmutex);//与其他读者进程互斥的访问readcount
if(readcount==0)
sem_wait(&mutex1);//与写者进程互斥的访问共享文件
readcount++;
sem_post(&rmutex);
sem_post(&num);
//reader
printf("Reader%d is reading!\n",i);
printf("Reader%d reads end!\n",i);
//退出文件后的处理
sem_wait(&rmutex);
readcount--;
if(readcount==0)
sem_post(&mutex1);
sem_post(&rmutex);
}
}
void Writer(void *arg)
{
int i=*(int *)arg;
while(b>0)
{
b--;
delay();
//进入共享文件前的准备
sem_wait(&wmutex);//保证多个写者进程能够互斥使用writecount
writecount++;
if(writecount==1)
sem_wait(&num);//用于禁止读者进程
sem_post(&wmutex);
//writer
sem_wait(&mutex1);//与其他所有进程互斥的访问共享文件
printf("writer%d is writing!\n",i);
printf("writer%d writes end!\n",i);
sem_post(&mutex1);
//退出共享文件后的处理
sem_wait(&wmutex);
writecount--;
if(writecount==0)
sem_post(&num);
sem_post(&wmutex);
}
}
int main()
{
int i;
pthread_t writer[N],reader[N];
sem_init(&wmutex,0,1);//互斥锁初始化
sem_init(&rmutex,0,1);
sem_init(&mutex1,0,1);
sem_init(&num,0,1);
for(i=0;i<5;i++)//创建线程
{
pthread_create(&reader[i],NULL,(void *)Reader,&r[i]);
}
for(i=0;i<2;i++)//创建线程
{
pthread_create(&writer[i],NULL,(void *)Writer,&w[i]);
}
for(i=0;i<2;i++)//等待线程
{
pthread_join(writer[i],NULL);
}
for(i=0;i<5;i++)//等待线程
{
pthread_join(reader[i],NULL);
}
sem_destroy(&rmutex); //互斥锁的销毁
sem_destroy(&wmutex);
sem_destroy(&mutex1);
sem_destroy(&num);
return 0;
}
[设计实现展示(可附截图说明)]
读者优先:
写者优先
读者写者公平竞争
[课程设计出现问题及解决方法]
出现的问题:
在写者优先时,老是会读者写者交叉显示,无法达到想要的效果
解决办法:
设置一个延时函数,每次延时1秒,即可保证顺序显示
【参考资料】
https://blog.csdn.net/william_munch/article/details/84256690