我有一些线程要写资源而有些要读取它。但是pthread_rwlock导致很多上下文切换。所以我想象一种避免它的方法。但我不确定它是否安全。
这是代码:
sig_atomic_t slot = 0;
struct resource {
sig_atomic_t in_use; /*Counter,if in_use, not zero*/
.....
} xxx[2];
int read_thread()
{
i = slot; /*avoid slot changes in process */
xxx[i].in_use++;
read(xxx[i]);
xxx[i].in_use--;
}
int write_thread()
{
mutex_lock; /*mutex between write threads */
if (slot == 0) {
while(xxx[1].in_use != 0); /*wait last read thread in slot 1*/
clear(xxx[1]);
write(xxx[1]);
slot = 1;
} else if (slot == 1) {
while(xxx[0].in_use != 0);
clear(xxx[0]);
write(xxx[0]);
slot = 0;
}
mutex_unlock;
}
这会有效吗?成本是存储的2倍和3个原子变量。 非常感谢!
答案 0 :(得分:1)
您的算法不是无锁的;作家使用自旋锁。
是否真的有必要进行双缓冲和旋转锁定?您是否可以使用(slot ^ 1)
作为书写位置,slot
作为阅读位置?在写完之后,作者会原子地改变slot
的值,从而“发布”它的写作。您可以通过这种方式连续多次读取相同的插槽,但如果这不是您想要的语义,那么您应该使用队列。
顺便说一下,sig_atomic_t
不提供多线程所需的原子类型。至少,您应将slot
声明为volatile sig_atomic_t
,并在阅读和写作时使用内存障碍。
答案 1 :(得分:0)
你的策略是让作家写一个不同于读者阅读的插槽。并且您在写入完成后切换读取插槽编号。但是,你将参加比赛。
slot reader writer1 writer2
---- ------ ------- -------
0 mutex_lock
i = 0
... slot=1
1 mutex_unlock mutex_lock
... clear(xxx[0])
xxx[0].in_use++
read(xxx[0]) write(xxx[0])
总的来说,这种策略可能会导致作家的饥饿(这是作家可能会永远旋转)。
但是,如果你愿意容忍,那么让xxx[]
成为指向resource
的2个指针的数组会更安全。让读者始终从xxx[0]
读取,让作者争论xxx[1]
的更新。当作者完成更新xxx[1]
时,它会在xxx[0]
和xxx[1]
上使用CAS。
struct resource {
sig_atomic_t in_use; /*Counter,if in_use, not zero*/
sig_atomic_t writer;
.....
} *xxx[2];
void read_thread()
{
resource *p = xxx[0];
p->in_use++;
while (p->writer) {
p->in_use--;
p = xxx[0];
p->in_use++;
}
read(*p);
p->in_use--;
}
void write_thread()
{
resource *p;
mutex_lock; /*mutex between write threads */
xxx[1]->writer = 1;
while(xxx[1]->in_use != 0); /*wait last read thread in slot 1*/
clear(xxx[1]);
write(xxx[1]);
xxx[1] = CAS(&xxx[0], p = xxx[0], xxx[1]);
assert(p == xxx[1]);
xxx[0]->writer = 0;
mutex_unlock;
}
如果你想避免编写器饥饿,但是你想要自旋锁的性能,你正在考虑使用自旋锁而不是互斥锁实现自己的读/写锁。谷歌搜索“读写自旋锁实现”指向this page,我发现这是一个有趣的读物。