读者写者模型
读者写者模型
读者写者
“读者-写者”问题是经典的同步问题,常用于解决多线程访问共享资源的同步控制。此问题主要是确保在多个线程并发访问共享资源(如文件或数据库)时,避免数据不一致。通常有两种策略:
读者优先:允许多个读者同时访问资源,但写者必须等待。
写者优先:写者一旦想要写入,所有读者都需要等待,优先满足写者请求。
读者优先
代码
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
sem_t resource; // 资源信号量,确保写者独占访问
sem_t read_count_sem; // 读计数器信号量,保证对读者计数的互斥访问
int read_count = 0; // 读者计数器
void *reader(void *arg) {
int reader_id = *(int *)arg;
free(arg);
while (1) {
// 进入临界区,增加读者计数
sem_wait(&read_count_sem);
read_count++;
if (read_count == 1) {
// 第一个读者请求访问资源,阻止写者
sem_wait(&resource);
}
sem_post(&read_count_sem);
// 模拟读操作
printf("Reader %d is reading\n", reader_id);
sleep(1); // 模拟读的耗时
// 退出临界区,减少读者计数
sem_wait(&read_count_sem);
read_count--;
if (read_count == 0) {
// 最后一个读者离开,允许写者访问资源
sem_post(&resource);
}
sem_post(&read_count_sem);
sleep(1); // 让其他线程有机会运行
}
return NULL;
}
void *writer(void *arg) {
int writer_id = *(int *)arg;
free(arg);
while (1) {
// 请求独占资源访问
sem_wait(&resource);
// 模拟写操作
printf("Writer %d is writing\n", writer_id);
sleep(2); // 模拟写的耗时
// 释放资源
sem_post(&resource);
sleep(1); // 让其他线程有机会运行
}
return NULL;
}
int main() {
pthread_t r_threads[5], w_threads[2];
// 初始化信号量
sem_init(&resource, 0, 1);
sem_init(&read_count_sem, 0, 1);
// 创建读者线程
for (int i = 0; i < 5; i++) {
int *id = malloc(sizeof(int));
*id = i + 1;
pthread_create(&r_threads[i], NULL, reader, id);
}
// 创建写者线程
for (int i = 0; i < 2; i++) {
int *id = malloc(sizeof(int));
*id = i + 1;
pthread_create(&w_threads[i], NULL, writer, id);
}
// 等待线程结束
for (int i = 0; i < 5; i++) {
pthread_join(r_threads[i], NULL);
}
for (int i = 0; i < 2; i++) {
pthread_join(w_threads[i], NULL);
}
// 销毁信号量
sem_destroy(&resource);
sem_destroy(&read_count_sem);
return 0;
}
代码说明:
- 资源信号量 resource:用于保证写者独占资源的访问权限。
- 读者计数器信号量 read_count_sem:保证对 read_count 的访问互斥。
- 读者函数 reader:每次读取前增加 read_count,第一个读者进入时阻止写者。
完成读取后减少 read_count,最后一个读者离开时释放写者的资源。 - 写者函数 writer:写者获取 resource,独占资源访问。
- 写入完成后释放 resource,允许其他读者或写者进入。
运行效果:
多个读者可以同时访问,且在没有写者时可以连续读取。
一旦有写者请求资源,写者会等待所有读者退出后独占资源。
写者优先
代码
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
sem_t resource; // 控制资源的信号量
sem_t read_count_sem; // 控制读者计数的信号量
sem_t write_count_sem; // 控制写者计数的信号量
int read_count = 0; // 当前读者计数
int write_count = 0; // 当前写者计数
void *reader(void *arg) {
int reader_id = *(int *)arg;
free(arg);
while (1) {
// 若有写者等待,读者需等待
sem_wait(&write_count_sem);
sem_post(&write_count_sem);
// 增加读者计数
sem_wait(&read_count_sem);
read_count++;
if (read_count == 1) {
sem_wait(&resource); // 阻止写者
}
sem_post(&read_count_sem);
// 模拟读操作
printf("Reader %d is reading\n", reader_id);
sleep(1);
// 减少读者计数
sem_wait(&read_count_sem);
read_count--;
if (read_count == 0) {
sem_post(&resource); // 最后一个读者离开后释放资源
}
sem_post(&read_count_sem);
sleep(1); // 让其他线程有机会运行
}
return NULL;
}
void *writer(void *arg) {
int writer_id = *(int *)arg;
free(arg);
while (1) {
// 增加写者计数
sem_wait(&write_count_sem);
write_count++;
if (write_count == 1) {
sem_wait(&resource); // 第一个写者到来时阻止读者
}
sem_post(&write_count_sem);
// 请求资源独占访问
printf("Writer %d is writing\n", writer_id);
sleep(2);
// 减少写者计数
sem_wait(&write_count_sem);
write_count--;
if (write_count == 0) {
sem_post(&resource); // 最后一个写者离开后允许读者进入
}
sem_post(&write_count_sem);
sleep(1); // 让其他线程有机会运行
}
return NULL;
}
int main() {
pthread_t r_threads[5], w_threads[2];
// 初始化信号量
sem_init(&resource, 0, 1);
sem_init(&read_count_sem, 0, 1);
sem_init(&write_count_sem, 0, 1);
// 创建读者线程
for (int i = 0; i < 5; i++) {
int *id = malloc(sizeof(int));
*id = i + 1;
pthread_create(&r_threads[i], NULL, reader, id);
}
// 创建写者线程
for (int i = 0; i < 2; i++) {
int *id = malloc(sizeof(int));
*id = i + 1;
pthread_create(&w_threads[i], NULL, writer, id);
}
// 等待线程结束
for (int i = 0; i < 5; i++) {
pthread_join(r_threads[i], NULL);
}
for (int i = 0; i < 2; i++) {
pthread_join(w_threads[i], NULL);
}
// 销毁信号量
sem_destroy(&resource);
sem_destroy(&read_count_sem);
sem_destroy(&write_count_sem);
return 0;
}
代码说明:
- 信号量 resource:控制对资源的独占访问。
- 信号量 read_count_sem:控制对读者计数器的互斥访问。
- 信号量 write_count_sem:控制对写者计数器的互斥访问。
- 读者函数 reader:检查是否有写者在等待,若有,读者会等待。每当第一个读者到来时,会阻止写者。
读操作完成后,如果是最后一个读者,则释放资源以允许写者访问。 - 写者函数 writer:写者到来时增加 write_count,第一个写者到达时阻止读者。
写操作完成后,减少 write_count,最后一个写者离开时释放资源以允许读者进入。
运行效果
当有写者请求访问时,写者优先于读者进行访问。
若写者和读者同时请求,写者会优先完成写操作,读者等待直至写者完成。