【问题标题】:Wrap std::thread call function包装 std::thread 调用函数
【发布时间】:2018-01-15 17:56:11
【问题描述】:

我想控制threads,所以每个新发布的thread都会先通过我的代码。
这样,在下面的示例中,每个使用runThread() 发出的新线程都会首先调用下面的函数runInnerThread()(我在其中进行了某种初始化),然后调用所需的函数。

我试过这样写:

#include <iostream>
#include <thread>

template<typename _Callable, typename... _Args>
void runThreadInner(_Callable&& __f, _Args&&... __args) {
    // My initializations...
    __f(__args...);
    // My finishing...
};

template<typename _Callable, typename... _Args>
bool runThread(_Callable&& __f, _Args&&... __args) {
    std::thread t(std::bind(&runThreadInner,
                            std::forward<_Callable>(__f),
                            std::forward<_Args>(__args)...));
}

int main() {
    runThread([]() {
        std::cout << std::this_thread::get_id() << "Threading...\n";
    });
    return 0;
}

我从编译器收到一个错误,抱怨推断 runThreadInner()模板

main.cpp: In instantiation of ‘bool runThread(_Callable&&, _Args&& ...) [with _Callable = main()::__lambda0; _Args = {}]’:
main.cpp:43:3:   required from here
main.cpp:27:38: error: no matching function for call to ‘bind(<unresolved overloaded function type>, main()::__lambda0)’
        std::forward<_Args>(__args)...));
                                      ^
main.cpp:27:38: note: candidates are:
In file included from /usr/include/c++/4.8/memory:79:0,
                 from main.cpp:2:
/usr/include/c++/4.8/functional:1655:5: note: template<class _Func, class ... _BoundArgs> typename std::_Bind_helper<std::__or_<std::is_integral<typename std::decay<_Tp>::type>, std::is_enum<typename std::decay<_Tp>::type> >::value, _Func, _BoundArgs ...>::type std::bind(_Func&&, _BoundArgs&& ...)
     bind(_Func&& __f, _BoundArgs&&... __args)
     ^
/usr/include/c++/4.8/functional:1655:5: note:   template argument deduction/substitution failed:
main.cpp:27:38: note:   couldn't deduce template parameter ‘_Func’
        std::forward<_Args>(__args)...));
                                      ^
In file included from /usr/include/c++/4.8/memory:79:0,
                 from main.cpp:2:
/usr/include/c++/4.8/functional:1682:5: note: template<class _Result, class _Func, class ... _BoundArgs> typename std::_Bindres_helper<_Result, _Func, _BoundArgs>::type std::bind(_Func&&, _BoundArgs&& ...)
     bind(_Func&& __f, _BoundArgs&&... __args)
     ^
/usr/include/c++/4.8/functional:1682:5: note:   template argument deduction/substitution failed:
main.cpp:27:38: note:   couldn't deduce template parameter ‘_Result’
        std::forward<_Args>(__args)...));

我尝试显式定义模板,但没有成功:

template<typename _Callable, typename... _Args>
bool runThread(_Callable&& __f, _Args&&... __args) {
    std::thread t(std::bind(&runThreadInner<_Callable, _Args>,
                            std::forward<_Callable>(__f),
                            std::forward<_Args>(__args)...));
}

有可能吗? 谢谢。

【问题讨论】:

  • 为什么要拨打bind?此外,您的 GCC 版本接近古老,并且没有完整的 C++11 支持。
  • 另外,我确实希望您的runThread 对它启动的线程有某种簿记,以便您以后可以join 它们。
  • 这不是问题,但是以下划线开头后跟大写字母的名称(_Callable 等)和包含两个连续下划线的名称(__f 等)保留供实现使用。不要在你的代码中使用它们。
  • 代码问题:缺少#includes。简单的语法错误(多余的右括号)。以下划线和大写字母开头的标识符保留给系统使用。 stackoverflow.com/questions/228783/…
  • 这个问题中的每一行代码,除了右括号和 main 的大部分内容,都有可能让我在代码审查中拒绝它的问题。这令人印象深刻。

标签: c++ multithreading c++11 stdthread


【解决方案1】:

简单易懂。

#include <thread>
#include <iostream>

template<typename Callable, typename... Args>
void runThreadInner(Callable&& f, Args&&... args) {
    // My initializations...
    f(args...);
    // My finishing...
};

template<typename Callable, typename... Args>
std::thread runThread(Callable&& f, Args&&... args) {
    std::thread t(&runThreadInner<Callable, Args...>, 
         std::forward<Callable> (f), std::forward<Args>(args)...);
    return t;
}
int main() {
    auto t = runThread([]() {
        std::cout << "Threading..." << std::endl;
    });
    t.join();
    return 0;
}

【讨论】:

  • 我强烈建议更改以两个下划线开头的名称以将其删除,因为它们是为编译器保留的。有迅猛龙。
  • @Alexander 嘿,我删除了以下划线开头的那些。但是,是的。我讨厌前导下划线和驼峰式。随意编辑。我的工作在这里完成。 :-)
  • 任何具有连续下划线或前导下划线后跟大写字母的变量都保留供编译器使用。我也讨厌 camelCase,但至少它不会导致我的代码突然因新的编译器或编译器版本而失败。 stackoverflow.com/a/228797/4131059
  • @Alexander 我只知道 _Capital 的事情。但后来我从来没有像那样远程做任何事情。想修好吗?
  • 在 C++17 之前,您的解决方案不需要 std::thread 的复制构造函数吗?您通过左值 t 初始化返回值。
【解决方案2】:

您可能缺少#include &lt;functional&gt;(其中定义了std::bind)。

但是,std::bind 在您的情况下并不是真正需要的,因为您有 lambda,在很多方面都是 better

这是一个没有std::bind 的工作示例:

#include <iostream>
#include <functional>
#include <thread>
#include <utility>

template <typename F>
struct MyTaskWrapper {
  F f;

  template <typename... T>
  void operator()(T&&... args) {
    std::cout << "Stuff before...\n";
    f(std::forward<T>(args)...);
    std::cout << "Stuff after...\n";
  }

};

template <typename F, typename... Args>
void runThread(F&& f, Args&&... args) {
  std::thread trd(MyTaskWrapper<F>{std::forward<F>(f)}, std::forward<Args>(args)...);
  trd.join();
}

int main() {
  runThread([] {
    std::cout << "Threading...\n";
  });
}

输出:

Stuff before...
Threading...
Stuff after...

Live demo

【讨论】:

    【解决方案3】:

    您的代码有问题。我几乎拒绝代码审查中的每一行。

    _Capital 不是合法的 C++。 __lower 也不是。停止复制系统和标准头文件所做的事情,它们是不允许的。他们这样做正是因为你现在被允许保证他们的代码不会与你做愚蠢的预处理器的事情重叠(以任何合法的方式)。

    template<typename Callable>
    std::thread runThread(Callable&& f) {
      std::thread t([=]{
        // My initializations...
        f();
        // My finishing...
      });
      return t;
    }
    
    int main() {
      auto t = runThread([]() {
        std::cout << std::this_thread::get_id() << "Threading...\n";
      });
      t.join();
    }
    

    如果有人想绑定参数,让他们在自己调用runThread时这样做。

    【讨论】:

    • 其实很简单。它使runThreadInner() 变得多余,因为它的代码可以在lambdalambda 之前和之后运行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-05
    • 1970-01-01
    • 2014-01-10
    • 2018-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多