Boost的无锁队列是否有可能丢失元素?

时间:2016-05-17 16:40:19

标签: c++ boost lock-free

我有一个使用boost::lockfree::queue单线程应用程序(来自Boost 1.57)。 (我使用队列的原因是它是一个库的一部分,它也用在多线程应用程序中。)

看来队列在某种程度上会丢失元素。

使用队列的类的行为如下:

  • 有两个单独的函数在队列上调用push。除了队列本身提供的内容之外,没有线程安全逻辑。
  • 一个函数消耗队列中的元素;它使用while(my_queue.pop(temp_element))循环消耗所有元素。此功能受互斥锁保护,以确保所有元素都转到单个使用者。但是,如果连续检测到两个重复的元素(与==相比),则跳过第二个元素。

我观察到元素似乎没有一致地通过系统传播,所以使用std::atomic_uint,我创建了一些计数器来检查相同数量的警报是否离开队列进入它:

  • 两种push方法中的每一种都有单独的计数器。我们会将这些称为pushed_method1pushed_method2
  • “处理”元素和“跳过”元素有单独的计数器。我们会将这些称为consumedskipped。在循环中,它们更新如下(重命名变量,并在[square brackets]中使用描述性伪代码):

    ElementType previous{[ initialize in invalid state ]};
    while (my_queue.pop(temp_element))
    {
      if (! [ check if `previous` is valid ]
          || previous != temp_element)
      {
        [ ... log a message ]
        [ ... insert the element into a vector to be returned ]
    
        ++consumed;
      }
      else
      {
        // ... log a message
    
        ++skipped;
      }
    
      previous = temp_element;
    }
    

循环之后,我检查计数器和队列的状态并进行健全性检查:

auto postconsume_pushed_method1 = pushed_method1.load();
auto postconsume_pushed_method2 = pushed_method2.load();
auto all_consumed = my_queue.empty();

assert(
    !all_consumed
  ||  postconsume_pushed_method1 + postconsume_pushed_method2
        == consumed + skipped);

此断言通常通过 - 但单线程应用有时会失败。 (在多线程的情况下,它有时也会失败,但我希望专注于单线程的情况来简化问题。)

在多线程的情况下,我可以想象操作正在重新排序,以便在load检查后发生一个或多个.empty(),但我希望不是因为我希望std::atomicboost::lockfree为这类事情包含内存栅栏。

但是,在单线程的情况下,这个应该无关紧要,因为当应用程序执行“时,从不 push使用“方法,并且警告队列应该总是在循环后为空(因为循环不会中断,直到pop返回false。”

我误解了什么吗?我的逻辑中有错误吗?我在boost::lockfree::queue找到了一个错误的机会很小吗?

1 个答案:

答案 0 :(得分:3)

队列是无锁的,因此当队列满了时push不会阻塞。

lockfree::queue::push返回一个布尔值,告诉该元素是否已将其放入队列中。当队列已满时,push会返回false,因此您可能希望检查此情况。