【问题标题】:Can I have a boost lock-free queue of lambdas?我可以有一个无锁的 lambda 队列吗?
【发布时间】:2014-02-19 18:22:57
【问题描述】:

我正在尝试实现一个可以跨多个线程工作的消息传递系统。 boost::lockfree::queue 似乎是一个不错的方向,不幸的是,我无法创建 std::functionboost::function 类型的队列,因为显然它们没有琐碎的赋值和析构函数,这是 boost::lockfree::queue 的要求.

我的以下代码:

#include <cassert>
//#include <functional>
#include <iostream>

#include <boost/function.hpp>
#include <boost/lockfree/queue.hpp>

int main()
{
  boost::lockfree::queue<boost::function<void(int)>> queue;
  assert(queue.is_lock_free());

  for(int j = 0; j < 50; ++j) {
    queue.push([] (int i) { std::cout << i << std::endl; });
  }

  int i = 0;
  boost::function<void(int)> functor;
  while (queue.pop(functor)) {
    functor(i++);
  }
}

产生以下输出:

In file included from /usr/include/boost/integer.hpp:23:0,
                 from /usr/include/boost/function/function_base.hpp:21,
                 from /usr/include/boost/function/detail/prologue.hpp:17,
                 from /usr/include/boost/function.hpp:24,
                 from lockfree.cpp:5:
/usr/include/boost/lockfree/queue.hpp: In instantiation of ‘class boost::lockfree::queue<boost::function<void(int)> >’:
lockfree.cpp:10:54:   required from here
/usr/include/boost/lockfree/queue.hpp:81:5: error: static assertion failed (boost::has_trivial_destructor<T>::value)
     BOOST_STATIC_ASSERT((boost::has_trivial_destructor<T>::value));
     ^
/usr/include/boost/lockfree/queue.hpp:85:5: error: static assertion failed (boost::has_trivial_assign<T>::value)
     BOOST_STATIC_ASSERT((boost::has_trivial_assign<T>::value));

有什么办法可以做到吗?

【问题讨论】:

    标签: c++ multithreading boost c++11 lambda


    【解决方案1】:

    如果可以的话,您仍然可以使用带有原始函数指针的 stateless lambda:

    #include <cassert>
    #include <functional>
    #include <iostream>
    
    #include <boost/function.hpp>
    #include <boost/lockfree/queue.hpp>
    
    int main() {
        //boost::lockfree::queue<boost::function<void(int)>> queue(50);
        boost::lockfree::queue<void (*)(int)> queue(50);
        assert(queue.is_lock_free());
    
        for (int j = 0; j < 50; ++j) {
            queue.push([](int i) { std::cout << i << std::endl; });
        }
    
        int i = 0;
        boost::function<void(int)> functor;
        while (queue.pop(functor)) {
            functor(i++);
        }
    }
    

    http://coliru.stacked-crooked.com/a/d57770e3be029760

    【讨论】:

    • 这样安全吗?基本上你持有一个指向临时函数的指针?
    • @Nim,说实话,我不确定......因为无状态 lambda 只是原始函数,我“认为”它们将与程序一样长,就像字符串文字存在......但我不知道是否有标准的引用支持这一点。
    • @Nim,顺便说一句,这是一个有趣的问题 =)
    • 谢谢!这很有趣。在生产中,我会将这些函数存储为类成员并引用它们,因为我不想做很多堆分配......所以在你的例子中它们是临时的这一事实不应该与这个想法冲突。
    • @FrançoisBeaune 它只工作 b/c 它是无状态的,可以转换为裸函数指针。当有状态时,就像您尝试做的那样,这种转换是不可能的。
    【解决方案2】:

    您可以使用指针来模拟普通类型。 (这适用于所有 lambdas)

    #include <cassert>
    #include <functional>
    #include <iostream>
    
    #include <boost/function.hpp>
    #include <boost/lockfree/queue.hpp>
    
    int main()
    {
      boost::lockfree::queue<boost::function<void(int)>*> queue(3);
      assert(queue.is_lock_free());
    
      for(int j = 0; j < 50; ++j) {
        auto function = [] (int i) { std::cout << i << std::endl; };
        queue.push(new boost::function<void(int)>(function));
      }
    
      int i = 0;
      boost::function<void(int)> * functor;
      while (queue.pop(functor)) {
        functor->operator()(i++);
        delete functor;
      }
    }
    

    【讨论】:

    • 如果queue.push(new boost::function&lt;void(int)&gt;(function)); 失败,则有一个新函数会泄漏内存。在示例中,push 被包装到一个 while 循环中,例如 while (!queue.push(value));。这可能很危险。
    【解决方案3】:

    boost::function 不满足无锁队列的要求:

    • 简单的复制构造函数
    • 琐碎的赋值
    • 微不足道的析构函数

    邮件列表中的thread 描述了您遇到的相同问题,解决方案可能是使用可以复制的仿函数对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-16
      • 1970-01-01
      • 1970-01-01
      • 2017-04-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多