boost::lockfree::函数队列?

Posted

技术标签:

【中文标题】boost::lockfree::函数队列?【英文标题】:boost::lockfree::queue of functions? 【发布时间】:2017-06-11 10:09:20 【问题描述】:

我想构建一个简单的boost::lockfree::queue 函数,它不带参数也不返回值。

似乎boost::lockfree::queue 要求项目类型可以简单地分配和破坏,而boost::function<void ()> 不幸地不满足这些要求。

本着https://***.com/a/21406186/393756 的精神,我现在正尝试通过boost::lockfree::queue 的普通函数指针来实现这一点:

boost::lockfree::queue<void (*)()> queue;

我可以将boost::function&lt;void ()&gt; 推入此队列吗?如果可以,如何?

【问题讨论】:

【参考方案1】:

我可以将boost::function&lt;void()&gt; 推入此队列吗?

不是直接的,因为boost::function&lt;void()&gt; 是一个重量级的拥有类型擦除的包装器,它不能隐式转换为函数指针,并且还存储一些数据。

如果您需要一个可以引用任何函数对象的普通可分配的普通可破坏类型,您可以实现一个function_view 类,该类指向某个函数对象而不拥有它。如果您注意生命周期并保证 function_view 始终指向“活动对象”,则可以安全地将其实例存储在队列中。

从概念上讲,function_view 是一对指针。我的"passing functions to functions" 文章中有一个实现,我将其粘贴在下面:

template <typename TReturn, typename... TArgs>
class function_view<TReturn(TArgs...)> final

private:
    using signature_type = TReturn(void*, TArgs...);

    void* _ptr;
    TReturn (*_erased_fn)(void*, TArgs...);

public:
    template <typename T, typename = std::enable_if_t<
                              std::is_callable<T&(TArgs...)> &&
                              !std::is_same<std::decay_t<T>, function_view>>>
    function_view(T&& x) noexcept : _ptr(void*)std::addressof(x)
    
        _erased_fn = [](void* ptr, TArgs... xs) -> TReturn 
            return (*reinterpret_cast<std::add_pointer_t<T>>(ptr))(
                std::forward<TArgs>(xs)...);
        ;
    

    decltype(auto) operator()(TArgs... xs) const
        noexcept(noexcept(_erased_fn(_ptr, std::forward<TArgs>(xs)...)))
    
        return _erased_fn(_ptr, std::forward<TArgs>(xs)...);
    
;

这个类通过了以下测试:

using type = function_view<void()>;
static_assert(is_trivially_assignable<type, type>);
static_assert(is_trivially_destructible<type>);

live example on wandbox

【讨论】:

但是你不得不担心视图没有看到一个有效的对象。这意味着您需要管理函数的生命周期。当您谈论线程时,很难退出。你怎么知道视图已经完成了它的工作并且你可以删除这个函数? @DavidHaim:这取决于 OP 的用例。如果所有权/生命周期层次结构不清楚,shared_ptr/unique_ptr 更合适。但是如果他有某种“父范围”,所有函数都存在,或者有某种“管理器”,他可以存储函数,那么视图可能更合适/更有效。 @VittorioRomeo 回答您的问题:在我的场景中,没有拥有这些功能的经理。【参考方案2】:

不,但您可以为此使用动态内存分配 + 类型擦除:

struct callback_back

   virtual void execute() = 0;
   ~callback_base() = default;

;

template<class F>
class callback

  private:
     F m_function;

  public:
    callback(F&& function) : m_function(std::forward<F>(function))

    virtual void execute() 
        m_function();
    



template<class F>
std::unique_ptr<callback_base> make_callback(F&& f)
    return std::unique_ptr<callback_base>(
       new callback<F>(std::forward<F>(f));
    );

callback_base 用作无例外可移动类型(又名boost::lockfree::queue&lt;std::unique_ptr&lt;callback_base&gt;&gt;)。

【讨论】:

请注意,unique_ptr 不能存储在 lockfree::queue 中,因为它不会被轻易破坏:wandbox.org/permlink/1E6MFC2KVB7xtbN7 哈哈。有趣的。那么在这种情况下,可以使用boost::lockfree::queue&lt;callback_base*&gt; 并将队列包装在一些非常薄的包装器中,该包装器获取并返回std::unique_ptr【参考方案3】:

到目前为止我发现的唯一方法是创建函数对象的原始指针

boost::lockfree::queue<std::function<void(void)> *> tasks_; // the queue
// let f = stack allocated std::function<T(T)> instance
tasks_.push(new std::function<void(void)>(f));
// pop
std::function<void(void)> * f;
tasks_.pop(f);
// execute in try/catch make sure to delete in case of exception?
(*f)();
// you should delete f here if you are done with it

// in the destructor of the class that owns tasks_ you should delete the remaining std::function instances

这里的挑战是何时删除此实例并考虑异常安全

【讨论】:

以上是关于boost::lockfree::函数队列?的主要内容,如果未能解决你的问题,请参考以下文章

evpp性能测试: 对无锁队列boost::lockfree::queue和moodycamel::ConcurrentQueue做一个性能对比测试

boost::lockfree::queue多线程读写实例

Boost lockfree deque 生产者与消费者多对多线程应用

使用 Boost 的 lockfree spsc_queue 时如何编译?

使用 sizeof(boost::lockfree::queue<std::string>) 时出错

boost::lockfree::spsc_queue 忙等待策略。有阻塞弹出吗?