参考https://blog.youkuaiyun.com/booksyhay/article/details/82762278
概念描述:
在一些程序中存在读者写者问题,也就是说,对某些资源的访问会 存在两种可能的情况,一种是访问必须是排它行的,就是独占的意思,这称作写操作;另一种情况就是访问方式可以是共享的,就是说可以有多个线程同时去访问某个资源,这种就称作读操作。这个问题模型是从对文件的读写操作中引申出来的。
读写锁比起mutex具有更高的适用性,具有更高的并行性,可以有多个线程同时占用读模式的读写锁,但是只能有一个线程占用写模式的读写锁,读写锁的三种状态:
1.当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞
2.当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是以写模式对它进行加锁的线程将会被阻塞
3.当读写锁在读模式的锁状态时,如果有另外的线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁的请求,这样可以避免读模式锁长期占用,而等待的写模式锁请求则长期阻塞。
读写锁最适用于对数据结构的读操作次数多于写操作的场合,因为,读模式锁定时可以共享,而写模式锁定时只能某个线程独占资源,因而,读写锁也可以叫做个共享-独占锁。
处理读者-写者问题的两种常见策略是强读者同步(strong reader synchronization)和强写者同步(strong writer synchronization). 在强读者同步中,总是给读者更高的优先权,只要写者当前没有进行写操作,读者就可以获得访问权限;而在强写者同步中,则往往将优先权交付给写者,而读者只能等到所有正在等待的或者是正在执行的写者结束以后才能执行。关于读者-写者模型中,由于读者往往会要求查看最新的信息记录,所以航班订票系统往往会使用强写者同步策略,而图书馆查阅系统则采用强读者同步策略。
解决方案:
初始化:
readers计数器记录房间里有多少读者。 互斥锁mutex保护共享的计数器。
osSemaphoreId_t sem_mutex;
sem_mutex = osSemaphoreNew(1, 1, NULL);
osSemaphoreId_t sem_roomEmpty;
sem_roomEmpty = osSemaphoreNew(1, 1, NULL);
int readers = 0;
void critical_writer()
{
printf("--write\r\n");
}
void critical_reader()
{
printf("--read\r\n");
}
写者线程:
读者代码类似于我们在上一节中看到的屏障代码。 我们会跟踪房间内的读者人数,以便我们可以给第一个到达的和最后一个离开的线程一个特殊的任务。
到达的第一个读者必须等待roomEmpty。 如果房间是空的,那么读者继续执行,同时禁止写者。 后续读者仍然可以进入,因为他们都不会试图在roomEmpty上等待。
如果已经有一个写者在房间里,这时有读者到了,它要等待房间变为空。 由于它拥有互斥锁,因此任何后续读者都会在互斥锁上排队。
while(1)
{
osSemaphoreAcquire(sem_roomEmpty, osWaitForever);
{
critical_writer();
}
osSemaphoreRelease(sem_roomEmpty);
osDelay(2000);
}
读者线程:
临界区之后的代码类似。 最后一个离开房间的读者把灯都关掉了-- 也就是说,它标志着roomEmpty,可能让等待的写者进入。
while(1)
{
osSemaphoreAcquire(sem_mutex, osWaitForever);
{
readers++;
if(readers == 1)
{
osSemaphoreAcquire(sem_roomEmpty, osWaitForever);
}
}
osSemaphoreRelease(sem_mutex);
critical_reader();
osSemaphoreAcquire(sem_mutex, osWaitForever);
{
readers--;
if(readers == 0)
{
osSemaphoreRelease(sem_roomEmpty);
}
}
osSemaphoreRelease(sem_mutex);
}
结论:
代码中,特意将写的延时为2000ms,读的延时为200ms,可以看到在10个读后,有一个写。