读者-写者问题
1、利用记录型信号量解决读者-写者问题
通过POSIX信号量实现。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#define NUM_READERS 5
#define NUM_WRITERS 3
#define READ_TIME 2
#define WRITE_TIME 1
sem_t rw_mutex; // 保护共享资源的互斥锁
sem_t mutex; // 保护读者计数的互斥锁
int read_count = 0; // 当前正在读取的读者数量
void *reader(void *arg) {
int reader_id = *((int *)arg);
while (1) {
// 请求读取锁
sem_wait(&mutex);
read_count++;
if (read_count == 1) {
sem_wait(&rw_mutex); // 第一个读者获得写锁(独占)
}
sem_post(&mutex);
// 读取操作
printf("Reader %d is reading.\n", reader_id);
sleep(READ_TIME);
// 释放读取锁
sem_wait(&mutex);
read_count--;
if (read_count == 0) {
sem_post(&rw_mutex); // 最后一个读者释放写锁
}
sem_post(&mutex);
sleep(1); // 读者休息一会儿再尝试读取
}
return NULL;
}
void *writer(void *arg) {
int writer_id = *((int *)arg);
while (1) {
// 请求写入锁
sem_wait(&rw_mutex);
// 写入操作
printf("Writer %d is writing.\n", writer_id);
sleep(WRITE_TIME);
// 释放写入锁
sem_post(&rw_mutex);
sleep(1); // 写者休息一会儿再尝试写入
}
return NULL;
}
int main() {
pthread_t readers[NUM_READERS], writers[NUM_WRITERS];
int reader_ids[NUM_READERS], writer_ids[NUM_WRITERS];
// 初始化信号量
sem_init(&rw_mutex, 0, 1);
sem_init(&mutex, 0, 1);
// 创建读者线程
for (int i = 0; i < NUM_READERS; i++) {
reader_ids[i] = i;
pthread_create(&readers[i], NULL, reader, &reader_ids[i]);
}
// 创建写者线程
for (int i = 0; i < NUM_WRITERS; i++) {
writer_ids[i] = i;
pthread_create(&writers[i], NULL, writer, &writer_ids[i]);
}
// 等待线程结束(实际上这些线程是无限循环的,这里只是为了示例)
for (int i = 0; i < NUM_READERS; i++) {
pthread_join(readers[i], NULL);
}
for (int i = 0; i < NUM_WRITERS; i++) {
pthread_join(writers[i], NULL);
}
// 销毁信号量
sem_destroy(&rw_mutex);
sem_destroy(&mutex);
return 0;
}
信号量初始化:
rw_mutex
:用于保护共享资源的互斥锁,初始值为1,表示资源可用。mutex
:用于保护读者计数read_count
的互斥锁,初始值为1,表示可访问。
读者线程:
- 使用
sem_wait(&mutex)
请求访问读者计数互斥锁。 - 增加
read_count
,如果这是第一个读者,则请求rw_mutex
锁,独占资源。 - 释放
mutex
锁。 - 执行读取操作。
- 再次请求
mutex
锁,减少read_count
,如果这是最后一个读者,则释放rw_mutex
锁。 - 释放
mutex
锁。
写者线程:
- 直接请求
rw_mutex
锁,独占资源。 - 执行写入操作。
- 释放
rw_mutex
锁。
主函数:
- 创建读者和写者线程。
- 等待线程结束(这里示例代码中的线程是无限循环的,实际使用时可能需要加入退出条件)。
- 销毁信号量
2、利用信号量集机制解决读者-写者问题
使用了信号量(semaphores)来管理对共享资源的访问。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#define NUM_READERS 5
#define NUM_WRITERS 3
#define READ_TIME 2
#define WRITE_TIME 1
sem_t rw_mutex; // 保护共享资源的互斥锁
sem_t mutex; // 保护读者计数的互斥锁
int read_count = 0; // 当前正在读取的读者数量
void *reader(void *arg) {
int reader_id = *((int *)arg);
while (1) {
sem_wait(&mutex);
read_count++;
if (read_count == 1) {
sem_wait(&rw_mutex);
}
sem_post(&mutex);
printf("Reader %d is reading.\n", reader_id);
sleep(READ_TIME);
sem_wait(&mutex);
read_count--;
if (read_count == 0) {
sem_post(&rw_mutex);
}
sem_post(&mutex);
sleep(1);
}
return NULL;
}
void *writer(void *arg) {
int writer_id = *((int *)arg);
while (1) {
sem_wait(&rw_mutex);
printf("Writer %d is writing.\n", writer_id);
sleep(WRITE_TIME);
sem_post(&rw_mutex);
sleep(1);
}
return NULL;
}
int main() {
pthread_t readers[NUM_READERS], writers[NUM_WRITERS];
int reader_ids[NUM_READERS], writer_ids[NUM_WRITERS];
sem_init(&rw_mutex, 0, 1);
sem_init(&mutex, 0, 1);
for (int i = 0; i < NUM_READERS; i++) {
reader_ids[i] = i;
pthread_create(&readers[i], NULL, reader, &reader_ids[i]);
}
for (int i = 0; i < NUM_WRITERS; i++) {
writer_ids[i] = i;
pthread_create(&writers[i], NULL, writer, &writer_ids[i]);
}
for (int i = 0; i < NUM_READERS; i++) {
pthread_join(readers[i], NULL);
}
for (int i = 0; i < NUM_WRITERS; i++) {
pthread_join(writers[i], NULL);
}
sem_destroy(&rw_mutex);
sem_destroy(&mutex);
return 0;
}
变量定义
NUM_READERS
和NUM_WRITERS
:分别定义了读者的数量和写者的数量。READ_TIME
和WRITE_TIME
:分别定义了读者读取和写者写入共享资源所需的时间(以秒为单位)。rw_mutex
:一个信号量,用于保护对共享资源的访问,确保写者在写入时具有独占权。mutex
:一个信号量,用于保护对read_count
(当前正在读取的读者数量)的访问,确保读者计数操作的原子性。read_count
:当前正在读取的读者数量。
读者线程(reader
函数)
- 每个读者线程首先获取
mutex
信号量,以确保对read_count
的访问是原子的。 - 然后,读者增加
read_count
的值。 - 如果这是第一个读者(
read_count == 1
),它会获取rw_mutex
信号量,以阻止写者和其他读者开始读取,直到当前读者组完成读取。 - 释放
mutex
信号量,允许其他读者修改read_count
。 - 读者开始读取(通过
sleep(READ_TIME)
模拟读取时间)。 - 再次获取
mutex
信号量,准备修改read_count
。 - 减少
read_count
的值。 - 如果这是最后一个读者(
read_count == 0
),它会释放rw_mutex
信号量,允许写者开始写入。 - 释放
mutex
信号量。 - 休眠一段时间(
sleep(1)
),然后重复上述过程。
写者线程(writer
函数)
- 每个写者线程首先获取
rw_mutex
信号量,以确保对共享资源的独占访问。 - 写者开始写入(通过
sleep(WRITE_TIME)
模拟写入时间)。 - 释放
rw_mutex
信号量,允许其他读者或写者访问共享资源。 - 休眠一段时间(
sleep(1)
),然后重复上述过程。
主函数(main
)
- 初始化
rw_mutex
和mutex
信号量。 - 创建读者线程和写者线程,每个线程都运行其相应的函数(
reader
或writer
)。 - 使用
pthread_join
等待所有读者和写者线程完成(实际上,由于线程在一个无限循环中运行,这里的pthread_join
会永远阻塞)。 - 销毁
rw_mutex
和mutex
信号量。
注意
- 这段代码中的读者和写者线程在一个无限循环中运行,这意味着程序永远不会自然结束。在实际应用中,你可能需要实现一种机制来优雅地停止这些线程。
- 使用
sleep
函数来模拟读取和写入时间是出于简化的目的。在实际应用中,你可能会有更复杂的逻辑来处理读取和写入操作。 pthread_join
的调用意味着主线程将等待所有读者和写者线程完成,但由于这些线程在无限循环中运行,程序将不会自然结束。