2012-06-28 31 views
1

我有一些线程来写资源和一些读取它。但pthread_rwlock导致大量的上下文切换。所以我想象一种避免它的方法。但我不确定它是否安全。原子变量可以代替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

什么阻止在阅读时进行阅读?阅读从不等待。 – stark

+0

单线程内存屏障环形缓冲区如何? – 2012-07-03 08:48:36

回答

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,我发现这是一个有趣的阅读。

+0

这真是太棒了 – HardySimpson

+0

您的建议解决方案与您在OP代码中标识的方法类似 - 阅读器可以加载指针值“xxx [0]”,然后写入器交换“xxx [1]”和“xxx [ 0]',那么另一个写入者进来并开始修改新的'xxx [1]'(旧的'xxx [0]'),然后读取器将指针解除引用并执行增量操作。 – caf

+0

@caf:谢谢你!我修改了算法。问候 – jxh

1

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

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

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

+0

谢谢你的sig_atomic_t,是的,它不是原子的。 – HardySimpson