为什么这个MPMC队列没有锁定免费?

时间:2018-01-18 02:13:01

标签: multithreading algorithm parallel-processing lock-free

作者说,实施的in this article的MPMC队列不是无锁的,并且阻塞了生产者/消费者队列。

  

根据分类,基于阵列的MPMC失败了   溢出,不需要GC,没有优先级,因果FIFO,阻塞   生产者和消费者排队。算法非常简单   快速。它在官方意义上并非无锁定,只是通过实施   没有互斥体的原子RMW操作方法。

但根据这个(http://www.1024cores.net/home/lock-free-algorithms/introduction):

  

锁定自由   锁定自由意味着整个系统向前发展   无论如何。每个线程的前进进度是   不保证(也就是说,个别线程可能会饿死)。

     

无锁算法的一个例子是:

void stack_push(stack * s, node * n) {
  node * head;
  do {
    head = s - > head;
    n - > next = head;
  }
  while (!atomic_compare_exchange(s - > head, head, n));
}
  

可以看出,一个线程可以"旋转"在理论上在循环中   无限地。但循环的每次重复都意味着其他一些线程   已经取得了进展(也就是说,成功地将节点推送到了   堆)。阻塞/中断/终止线程无法阻止   其他线程的进展。因此,整个系统   毫无疑问会取得进展。

似乎上面的MPMC队列在无锁中满足这些假设。但为什么说它在官方意义上不是无锁的,让我感到困惑。想知道我是否想念这里的东西?

不熟悉c ++,the c# port version here是阻塞算法,也不是无锁?

文章中的代码引用于此处:

template<typename T>

class mpmc_bounded_queue

{

public:

  mpmc_bounded_queue(size_t buffer_size)

    : buffer_(new cell_t [buffer_size])

    , buffer_mask_(buffer_size - 1)

  {

    assert((buffer_size >= 2) &&

      ((buffer_size & (buffer_size - 1)) == 0));

    for (size_t i = 0; i != buffer_size; i += 1)

      buffer_[i].sequence_.store(i, std::memory_order_relaxed);

    enqueue_pos_.store(0, std::memory_order_relaxed);

    dequeue_pos_.store(0, std::memory_order_relaxed);

  }

  ~mpmc_bounded_queue()

  {

    delete [] buffer_;

  }

  bool enqueue(T const& data)

  {

    cell_t* cell;

    size_t pos = enqueue_pos_.load(std::memory_order_relaxed);

    for (;;)

    {

      cell = &buffer_[pos & buffer_mask_];

      size_t seq = 

        cell->sequence_.load(std::memory_order_acquire);

      intptr_t dif = (intptr_t)seq - (intptr_t)pos;

      if (dif == 0)

      {

        if (enqueue_pos_.compare_exchange_weak

            (pos, pos + 1, std::memory_order_relaxed))

          break;

      }

      else if (dif < 0)

        return false;

      else

        pos = enqueue_pos_.load(std::memory_order_relaxed);

    }

    cell->data_ = data;

    cell->sequence_.store(pos + 1, std::memory_order_release);

    return true;

  }

  bool dequeue(T& data)

  {

    cell_t* cell;

    size_t pos = dequeue_pos_.load(std::memory_order_relaxed);

    for (;;)

    {

      cell = &buffer_[pos & buffer_mask_];

      size_t seq = 

        cell->sequence_.load(std::memory_order_acquire);

      intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1);

      if (dif == 0)

      {

        if (dequeue_pos_.compare_exchange_weak

            (pos, pos + 1, std::memory_order_relaxed))

          break;

      }

      else if (dif < 0)

        return false;

      else

        pos = dequeue_pos_.load(std::memory_order_relaxed);

    }

    data = cell->data_;

    cell->sequence_.store

      (pos + buffer_mask_ + 1, std::memory_order_release);

    return true;

  }

private:

  struct cell_t

  {

    std::atomic<size_t>   sequence_;

    T                     data_;

  };

  static size_t const     cacheline_size = 64;

  typedef char            cacheline_pad_t [cacheline_size];

  cacheline_pad_t         pad0_;

  cell_t* const           buffer_;

  size_t const            buffer_mask_;

  cacheline_pad_t         pad1_;

  std::atomic<size_t>     enqueue_pos_;

  cacheline_pad_t         pad2_;

  std::atomic<size_t>     dequeue_pos_;

  cacheline_pad_t         pad3_;

  mpmc_bounded_queue(mpmc_bounded_queue const&);

  void operator = (mpmc_bounded_queue const&);

}; 

0 个答案:

没有答案