1

我有一些线程要写入资源,有些线程要读取它。但是 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 个原子变量。非常感谢!

4

2 回答 2

1

你的算法不是无锁的;作者使用自旋锁。

真的有必要做双缓冲和自旋锁吗?您可以改为(slot ^ 1)用作写入槽和slot读取槽吗?写入后,作者将自动更改 的值slot,从而“发布”其写入。您可以通过这种方式连续多次读取同一个插槽,但如果这不是您想要的语义,那么您应该使用队列。

顺便说一句, asig_atomic_t不提供多线程所需的原子性类型。至少,您应该声明slotvolatile sig_atomic_t,并在读写时使用内存屏障。

于 2012-06-29T14:45:01.377 回答
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])

但是,总的来说,这种策略可能会导致作家挨饿(即作家可能永远旋转)。

但是,如果您愿意容忍这种情况,那么 letxxx[]是一个由 2 个指针组成的数组会更安全resource。让读者随时阅读xxx[0],让作者争相更新xxx[1]。当编写器完成更新时,它会在和xxx[1]上使用 CAS 。xxx[0]xxx[1]

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;
}

如果您想避免写入器饥饿,但又想要自旋锁的性能,那么您正在考虑使用自旋锁而不是互斥锁来实现自己的读取器/写入器锁。谷歌搜索“读写自旋锁实现”指向这个页面,我发现这是一个有趣的阅读。

于 2012-06-28T03:01:17.583 回答