【问题标题】:Wait-free ring buffer免等待环形缓冲区
【发布时间】:2013-11-22 12:35:14
【问题描述】:

我有一个场景,我可以有多个生产者但一个环形缓冲区的消费者。这是源代码:

typedef struct ring_buffer_t {
    uint8_t *data;
    uint32_t element_size;
    uint32_t element_count;
    uint32_t head;
    uint32_t tail;
    uint32_t mask;
} ring_buffer;

uint32_t ring_buffer_put(ring_buffer *buf, void *data_element) {
    int i;
    uint32_t status = 0;
    uint8_t *buf_pointer;
    uint8_t *element = (uint8_t *) data_element;

    if (buf && data_element) {

        buf_pointer = &buf->data[(buf->head & buf->mask) * buf->element_size];
        for (i = 0; i < buf->element_size; ++i) {
            buf_pointer[i] = element[i];
        }

        status = 1;
        __sync_fetch_and_add(&buf->head, 1);

    }

    return status;
}

uint32_t ring_buffer_size(ring_buffer *buf) {

    return buf->head - buf->tail;
}

uint32_t ring_buffer_empty(ring_buffer *buf) {

    return (ring_buffer_size(buf) == 0);
}

void *ring_buffer_get(ring_buffer *buf) {
    void *element;
    //preserve the invariant that tail is always <= head
    if (ring_buffer_empty(buf)) {
        return 0;
    }

    element = &buf->data[(buf->tail & buf->mask) * buf->element_size];
    __sync_fetch_and_add(&buf->tail, 1);
    return element;
}

问题陈述允许我覆盖旧条目,例如这就是为什么我使用 and 并且在放置时没有检查缓冲区是否已满 - 我只是覆盖最旧的条目。但是,我很难推断这是否是线程安全的。如前所述 - 重要的是这是无等待的(根据定义分别是无锁的),因为生产者无法承受阻塞。大小将始终是 2 的幂,这就是索引期间 & 起作用的原因。

任何意见将不胜感激?

【问题讨论】:

  • 我看不出它如何是线程安全的, __sync_fetch_and_add 确实是原子的,但是什么可以防止竞争条件?我没看到。
  • 唯一的免等待环形缓冲区实现是在缓冲区溢出时调用 exit(1)。
  • 从技术上讲,缓冲区永远不会被填满,因为覆盖旧条目是完全可以的。

标签: c multithreading thread-safety locking embedded


【解决方案1】:

不,您的实现不是线程安全的。

如果两个线程同时put 元素,它们最终可能会在同一个位置写入,甚至可以在一个位置写入一半记录,而在下一个位置写入另一半。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-04
  • 2019-01-17
  • 2018-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-27
相关资源
最近更新 更多