它们是如何实现的,尤其是在 pthreads 的情况下。他们在后台使用了哪些 pthread
同步 API?一些伪代码将不胜感激。
最佳答案
我有一段时间没有进行任何 pthreads 编程,但是当我这样做时,我从未使用过 POSIX 读/写锁。问题是大多数时候互斥锁就足够了:即。您的关键部分很小,而且该区域的性能并不重要,以至于双重屏障值得担心。
在性能成为问题的情况下,通常使用原子操作(通常作为编译器扩展提供)是更好的选择(即额外的障碍是问题,而不是关键部分的大小)。
当您消除所有这些情况时,您剩下的情况是您有特定的性能/公平性/rw-bias 要求,需要真正的 rw-lock;那就是当您发现 POSIX rw-lock 的所有相关性能/公平性参数未定义且特定于实现时。在这一点上,您通常最好自己实现,这样您就可以确保满足适当的公平性/rw-bias 要求。
基本算法是计算临界区中每个线程的数量,如果线程尚未被允许访问,则将其分流到适当的队列中等待。您的大部分工作将用于在为两个队列提供服务之间实现适当的公平/偏见。
下面的类 C 类 pthreads 伪代码说明了我想说的。
struct rwlock {
mutex admin; // used to serialize access to other admin fields, NOT the critical section.
int count; // threads in critical section +ve for readers, -ve for writers.
fifoDequeue dequeue; // acts like a cond_var with fifo behaviour and both append and prepend operations.
void *data; // represents the data covered by the critical section.
}
void read(struct rwlock *rw, void (*readAction)(void *)) {
lock(rw->admin);
if (rw->count < 0) {
append(rw->dequeue, rw->admin);
}
while (rw->count < 0) {
prepend(rw->dequeue, rw->admin); // Used to avoid starvation.
}
rw->count++;
// Wake the new head of the dequeue, which may be a reader.
// If it is a writer it will put itself back on the head of the queue and wait for us to exit.
signal(rw->dequeue);
unlock(rw->admin);
readAction(rw->data);
lock(rw->admin);
rw->count--;
signal(rw->dequeue); // Wake the new head of the dequeue, which is probably a writer.
unlock(rw->admin);
}
void write(struct rwlock *rw, void *(*writeAction)(void *)) {
lock(rw->admin);
if (rw->count != 0) {
append(rw->dequeue, rw->admin);
}
while (rw->count != 0) {
prepend(rw->dequeue, rw->admin);
}
rw->count--;
// As we only allow one writer in at a time, we don't bother signaling here.
unlock(rw->admin);
// NOTE: This is the critical section, but it is not covered by the mutex!
// The critical section is rather, covered by the rw-lock itself.
rw->data = writeAction(rw->data);
lock(rw->admin);
rw->count++;
signal(rw->dequeue);
unlock(rw->admin);
}
类似于上面的代码是任何 rwlock 实现的起点。考虑一下您的问题的性质,并用确定接下来应该唤醒哪一类线程的适当逻辑替换出列。根据应用,通常允许有限数量/期间的读者超越作者,反之亦然。
当然,我的一般偏好是完全避免读写锁;通常通过使用原子操作、互斥锁、STM、消息传递和持久数据结构的某种组合。然而,有时您真正需要的是读写锁,当您这样做时,了解它们的工作原理很有用,所以我希望这对您有所帮助。
编辑 - 作为对(非常合理的)问题的回应,我在上面的伪代码中在哪里等待:
我假设 dequeue 实现包含等待,所以在 append(dequeue, mutex)
或 prepend(dequeue, mutex)
中的某处有一个 block 代码如下:
while(!readyToLeaveQueue()) {
wait(dequeue->cond_var, mutex);
}
这就是为什么我将相关互斥量传递给队列操作的原因。
关于c - pthread 中的读/写锁是如何实现的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11032450/