如何在pthreads的情况下实现它们。他们在引擎盖下使用了哪些pthread
同步API?一点点伪代码将不胜感激。
答案 0 :(得分:9)
我暂时没有做任何pthreads编程,但是当我这样做时,我从未使用过POSIX读/写锁。问题是大多数情况下互斥锁就足够了:即。你的关键部分很小,而且这个区域对性能不太重要,以至于双重障碍值得担心。
在性能成为问题的情况下,通常使用原子操作(通常作为编译器扩展提供)是更好的选择(即额外的障碍是问题,而不是关键部分的大小)。
当你消除所有这些情况时,你会遇到需要真正的rw-lock的特定性能/公平性/ rw-bias要求的情况;那就是当你发现POSIX rw-lock的所有相关性能/公平参数都是未定义的和特定于实现的时候。在这一点上,你通常最好不要实现自己的,这样你就可以确保满足适当的公平/ rw偏见要求。
基本算法是保持关键部分中每个部分的数量,并且如果还不允许访问线程,则将其分流到适当的队列以等待。您的大部分努力都是在为两个队列提供服务之间实现适当的公平/偏见。
以下类似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实现的起点。考虑一下你的问题的本质,并用适当的逻辑替换dequeue,该逻辑确定接下来要唤醒哪个线程类。根据申请,允许有限数量/期限的读者超越作者或反之亦然。
当然,我的首选是完全避免使用rw-lock;通常使用原子操作,互斥体,STM,消息传递和持久数据结构的某种组合。然而,有时你真正需要的是一个锁定,当你这样做时,知道它们如何工作是有用的,所以我希望这有帮助。
编辑 - 回答(非常合理的)问题,我在哪里等待上面的伪代码:
我假设dequeue实现包含wait,所以在append(dequeue, mutex)
或prepend(dequeue, mutex)
内的某处有一行代码:
while(!readyToLeaveQueue()) {
wait(dequeue->cond_var, mutex);
}
这就是为什么我将相关的互斥锁传递给队列操作。
答案 1 :(得分:0)
每个实现可能会有所不同,但由于POSIX要求线程能够多次获取rwlock的读锁,通常它们必须默认支持读者。如果他们喜欢编写者,那么每当作者等待时,读者就会在第二次读取锁定尝试时死锁,除非实现可以确定读者已经有读锁定,但唯一的方法是确定存储所有线程的列表持有读锁,这在时间和空间要求上非常低效。