1.背景概念
读者优先策略
在读者优先策略中,允许多个读者同时访问共享资源,有读者在读时,写者必须等待,直到所有当前的读者和写者都完成访问。这种策略可以提高并发度,但可能导致写者饥饿。
写者优先策略
在写者优先策略中,一旦有写者等待,新的读者必须等待,直到所有当前的读者和写者都完成访问。这种策略可以减少写者的等待时间,但可能导致读者饥饿。
2.读写锁
- 读模式(共享模式)
- 多个线程可以同时以读模式持有读写锁。当一个线程以读模式获取到读写锁后,其他线程如果也想要以读模式获取该锁,是可以成功获取的。这是因为多个读操作通常不会相互干扰,不会破坏数据的一致性。例如,多个线程同时读取一个共享文件的内容,它们可以同时进行,不会有冲突。
- 写模式(独占模式)
- 只有一个线程可以以写模式持有读写锁。当一个线程以写模式获取到读写锁时,其他任何线程(无论是想要以读模式还是写模式获取该锁)都必须等待。这是因为写操作会修改共享资源,如果有其他线程同时进行读或写操作,可能会导致数据不一致。例如,当一个线程正在修改一个共享文件时,不允许其他线程同时读取或修改该文件。
在Linux中,读写锁的类型为 pthread_rwlock_t,其使用方法与互斥锁基本一致。
读写锁的创建
1. 可以使用PTHREAD_RWLOCK_INITIALIZER
初始化创建全局的读写锁,并且无需销毁。
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
参数:
rwlock
:指向要初始化的读写锁对象的指针。attr
:指向读写锁属性对象的指针,如果不需要特殊属性,可以设置为NULL
,使用默认属性。
函数成功时返回 0;失败时返回错误码。
加锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
pthread_rwlock_rdlock 用于给读写锁申请一个读锁。如果读写锁当前没有被写锁占用,那么调用线程可以立即获得读锁。如果读写锁当前被写锁占用,那么调用线程将被阻塞,直到写锁被释放。
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
pthread_rwlock_wrlock 用于给读写锁申请一个写锁。如果读写锁当前没有被任何锁占用,那么调用线程可以立即获得写锁。如果读写锁当前被读锁或写锁占用,那么调用线程将被阻塞,直到所有的读锁和写锁都被释放。
rwlock
:指向要申请读锁的读写锁对象的指针。
解锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
pthread_rwlock_unlock
函数用于释放之前通过 pthread_rwlock_rdlock
(读锁)或者 pthread_rwlock_wrlock
(写锁)获取的读写锁。
读写锁的销毁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
3.基于读写锁实现的读者写者模型
基于读写锁,我们来实现一个测试代码看看效果
#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
using namespace std;
int g_tickets = 100;
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; // 读写锁
struct ThreadData
{pthread_t tid;string name;
};
void *ReaderRoutinue(void *arg)
{ThreadData *reader = static_cast<ThreadData *>(arg);while (true){usleep(1000);pthread_rwlock_rdlock(&rwlock); // 读锁if (g_tickets > 0)cout << reader->name << " check a ticket-" << g_tickets << endl;else{pthread_rwlock_unlock(&rwlock);break;}pthread_rwlock_unlock(&rwlock);}return nullptr;
}
void *WriterRoutinue(void *arg)
{ThreadData *writer = static_cast<ThreadData *>(arg);while (true){usleep(2000);pthread_rwlock_wrlock(&rwlock); // 写锁if (g_tickets > 0)cout << writer->name << " get a ticket-" << g_tickets-- << endl;else{pthread_rwlock_unlock(&rwlock);break;}pthread_rwlock_unlock(&rwlock);}return nullptr;
}
int main()
{ThreadData readers[5];ThreadData writers[2];for (int i = 0; i < 5; i++){readers[i].name = "Reader-" + to_string(i + 1);pthread_create(&readers[i].tid, nullptr, ReaderRoutinue, &readers[i]);}for (int i = 0; i < 2; i++){writers[i].name = "Writer-" + to_string(i + 1);pthread_create(&writers[i].tid, nullptr, WriterRoutinue, &writers[i]);}for (int i = 0; i < 5; i++)pthread_join(readers[i].tid, nullptr);for (int i = 0; i < 2; i++)pthread_join(writers[i].tid, nullptr);return 0;
}