5

我有一个生产者一消费者模型,我需要生产者在数据可用时设置一个标志。我怀疑我可以在没有锁定共享标志的情况下逃脱,因为:

  • 生产者在设置之前从不检查值
  • 偶尔缺少对标志的更新不是问题(尽管我也可以使用原子操作来避免这种情况?)。

所以我的问题是,我该如何实现呢?我对 volatile 关键字的理解,以及像 __sync_synchronize() 这样的东西充其量是微不足道的,所以假设我知道的很少。具体来说,我希望能够确保及时在另一个线程中看到对标志的更改。

编辑:我在 Linux 上使用 GCC。

4

4 回答 4

1

使用两个变量:

volatile size_t elements_produced; // producer increments it when data is available
volatile size_t elements_consumed; // consumer increments it

新数据准确的时间是可用的elements_produced != elements_consumed。如果您需要无限量,那么另外在更新时对其进行修改。

produce(...) {
    elements_produced = (elements_produced + 1) % (max_elements_in_queue + 1);
}

consume(...) {
    elements_consumed = (elements_consumed + 1) % (max_elements_in_queue + 1);
}

不需要锁或原子。

这是单生产者、单消费者循环环形缓冲区的标准实现。

于 2012-10-08T20:10:07.787 回答
0

以便携式方式执行此操作实际上是不可能的。

但是,您可以使用各种编译器内部函数来实现它。

例如,对于 x86(-64) 上的 gcc,可能至少 ARM:

static int queued_work;

static void inc_queued_work()
{
    (void)__sync_add_and_fetch( &queued_work, 1 );
}

/*
  Decrement queued_work if > 0.
  Returns 1 if queued_work was non-equal to 0 before
  this function was called.
*/
static int dec_queued_work()
{
    /* Read current value and subtract 1.
       If the result is equal to -1, add 1 back and return 0.
    */
    if( __sync_sub_and_fetch( &queued_work, 1 ) == -1 )
    {
        __sync_fetch_and_add( &queued_work, 1 );
        return 0;
    }
    return 1;
}

一些 CPU:s 只支持 compare_and_swap。您也可以使用该 intrisic 来实现它:

/* Alternative solution using compare_and_swap  */
void inc_queued_work()
{
    do {
        int queued = queued_work;
        /* Try to write queued-1 to the variable. */
        if( __sync_bool_compare_and_swap( &queued_work,
                                         queued, queued+1 ) )
            return;
    } while( 1 );
}

int dec_queued_work()
{
    do {
        int queued = queued_work;
        if( !queued ) return 0;
        /* Try to write queued-1 to the variable. */
        if( __sync_bool_compare_and_swap( &queued_work, 
                                         queued, queued-1 ) )
            return queued;
    } while( 1 );
}

即使您有多个作者和读者,这些功能也应该可以工作。和朋友一起应用google到'sync_add_and_fetch'会给你很多参考文档

于 2012-10-12T19:45:57.460 回答
0

原子操作意味着(非常短暂地)阻塞其他原子操作,直到第一个操作完成。

我假设我们正在谈论线程,因此我们应该考虑互斥锁(但这也适用于进程和信号量)。互斥体(或信号量)可以在不尝试获取锁的情况下检查(读取)。

如果互斥体(信号量)的状态是已经被锁定,则继续进行其他操作,稍后再试。

于 2012-10-08T20:09:30.187 回答
-1

如果没有锁,两个线程将永远不会同步,并且消费者将永远旋转等待一个永远不会改变的值(因为该值被缓存,因此内存放置/获取永远不会发生)。

所以总而言之,你必须 a) 确保内存是从生产者那里写入的,b) 确保内存是由消费者读取的。这正是锁的作用,这就是你应该使用它们的原因。如果你对锁死了,你可以使用像 sleep 这样的函数,它可以保证唤醒后的缓存状态。

于 2012-10-08T21:10:16.247 回答