【问题标题】:C++11 thread wrapper functionC++11 线程包装函数
【发布时间】:2016-01-24 11:40:06
【问题描述】:

我想要一个包装线程函数,即一个由线程执行的函数,它做一些额外的事情,然后调用用户函数。

template<class F, class... Args>
void wrapper(F&& user_function, Args&&... args) {
  // do some extra stuff
  user_function(args); // maybe I need to forward args
  // do some extra stuff
}

好的,这可能是一个很好的包装器,所以我需要一个使用这个包装器功能并允许用户产生自己的线程的管理器:

class ThreadManager {
public:

  template<class F, class... Args>
  std::thread newThread(F&& f, Args&&... args) {
    return std::thread(thread_wrapper<F,Args...>, std::forward<F>(f), std::forward<Args>(args)...);
  }

};

这样,线程管理器应该生成一个使用包装函数的线程,该函数反过来做额外的工作并调用用户函数。

但编译器现在说:尝试使用已删除的函数。

错误在线程头中:

template <class _Fp, class ..._Args, size_t ..._Indices>
inline _LIBCPP_INLINE_VISIBILITY
void
__thread_execute(tuple<_Fp, _Args...>& __t, __tuple_indices<_Indices...>)
{
    __invoke(_VSTD::move(_VSTD::get<0>(__t)), _VSTD::move(_VSTD::get<_Indices>(__t))...);
}

我错过了什么/做错了什么?

[编辑]

使用测试:

void foo(int i) {
  std::cout << "foo: " << i << std::endl;
}

int main(int argc, const char *argv[]) {
  ThreadManager mgr;
  auto t = mgr.newThread(foo, 10);
  t.detach();

  std::this_thread::sleep_for(std::chrono::milliseconds(1000));

  return 0;
}

我正在使用带有 LLVM 编译器的 Xcode 7.1,但在 FreeBSD clang 3.3 上也失败了。

Xcode 错误是:

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/thread:337:5:错误:尝试使用已删除的函数 __invoke(_VSTD::move(_VSTD::get(__t)), _VSTD::move(_VSTD::get<_indices>(__t))...); ^ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/thread:347:5:注意:在函数模板特化'std::__1的实例化中: :__thread_execute' 在这里请求 __thread_execute(*__p, _Index()); ^ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/thread:359:42:注意:在函数模板特化'std::__1的实例化中: :__thread_proxy >' 在这里请求 int __ec = pthread_create(&__t_, 0, &__thread_proxy<_gp>, __p.get());

【问题讨论】:

  • 发布确切的错误。你使用什么编译器和版本?
  • 如果您使用普通函数而不是仿函数,您在 melpon.org 上的示例将失败...
  • 请注意,显式使用thread_wrapper&lt;F, Args...&gt;会将您完美的转发F&amp;&amp;Args&amp;&amp;参数变成实际的右值引用参数。

标签: multithreading c++11


【解决方案1】:

我不确定是什么导致了您的示例中的“尝试使用已删除的函数”,我得到了与 std::thread 的绑定机制相关的其他错误。

看来您为thread_wrapper 拼写模板参数的方式与std::thread 的构造函数不兼容——尤其是当它在内部使用简化的std::bind 时。完美转发的函数类型和std::decayed 函数指针的混合似乎让std::result_of 感到不安。

我们可以通过自己在newThread 中应用一些std::decay 来使其工作:

return std::thread( thread_wrapper<typename std::decay<F>::type,
                                   typename std::decay<Args>::type...>,
                    std::forward<F>(f), 
                    std::forward<Args>(args)... );

...但老实说,我不完全确定为什么有效。

或者,通过一些间接和更多转发,我们可以避免必须拼出模板参数。 我们只需要一个转发到 thread_wrapper 的函子(或 C++14 中的多态 lambda):

struct wrapper_helper {
    template<class F, class... Args>
    void operator()(F&& f, Args&&... args) const {
        thread_wrapper(std::forward<F>(f), std::forward<Args>(args)...);
    }
};

并在newThread中使用它:

return std::thread(wrapper_helper{}, std::forward<F>(f), std::forward<Args>(args)...);

这是完整示例,显示按预期工作的按值、引用和右值引用传递的参数:http://coliru.stacked-crooked.com/a/b75d5a264f583237

注意:对于像std::unique_ptr 这样的仅移动类型,您肯定希望将args... 转发到thread_wrapper

【讨论】:

  • 很好,似乎工作正常,但如果尝试传递 unique_ptr std::unique_ptr&lt;int&gt; p{new int(10)}; mgr.newThread(my_function, std::move(p)); // fails mgr.newThread(my_function, std::unique_ptr&lt;int&gt;{new int(10)}); // fails 会失败
  • @AntonelloSeprano 如果您不更改 thread_ wrapper 来转发参数,我会期待这一点 - 在这种情况下,它会尝试复制 unique_ptr 参数,但它不能。 Works on my compiler if I do forward
  • 是的,我没有在 thread_wrapper 中转发 args,但现在(通过转发)停止使用普通变量:coliru.stacked-crooked.com/a/1a8c80b265c915ff
  • @AntonelloSeprano 在将其传递给 std::thread 的构造函数之前,我能够通过将包装器包装在通用转发 lambda 中来摆脱它:coliru.stacked-crooked.com/a/fae52fcdaa522b8e
  • 的作用就像一个魅力,但它不是 C++11。没问题,当然 :) 我可以再添加一个 +1 吗? :D 我将您的回答设置为“有效”,请编辑您的答案并添加您的最后建议:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多