c++ - 如何将函数和参数包转发到 lambda、std::thread 的构造函数中?

标签 c++ multithreading c++11 lambda perfect-forwarding

我希望在 C++11 中实现一个“可中断”线程,类似于 this answer .

我已经参数化了我的可中断类,以便它可以包装任何具有类似构造函数的线程类(用于 std::thread,但也应该适用于 boost::thread 之类的东西(不用介意 boost::thread 已经有这个功能))。这部分应该不重要。

尽管链接的答案有一些我必须纠正的问题,但它对我来说很有意义,所以我想弄清楚我哪里出了问题。

我已包含相关来源和结果。

interruptible.hpp

#include <atomic>
#include <exception>
#include <thread>

class interrupted_exception : public virtual std::exception
{
public:
    char const *what() const noexcept { return "interrupted"; }
};


template <typename T>
class interruptible
{
public:
    template <typename F, typename... A>
    interruptible(F&& Function, A&&... Arguments) :
        Interrupted(false),
        Thread(
            [](std::atomic_bool *Interrupted, F&& Function, A&&... Arguments)
            {
                LocalInterrupted = Interrupted;
                Function(std::forward<A>(Arguments)...);
            },
            &this->Interrupted,
            std::forward<F>(Function),
            std::forward<A>(Arguments)...
        )
    { }

    void interrupt()            { this->Interrupted = true; }
    bool interrupted() const    { return this->Interrupted; }

    T *operator->()             { return &this->Thread; }

    static inline void check() noexcept(false)
    {
        if (!interruptible::LocalInterrupted)
            return;

        if (!interruptible::LocalInterrupted->load())
            return;

        throw interrupted_exception();
    }

private:
    static thread_local std::atomic_bool *LocalInterrupted;

    std::atomic_bool    Interrupted;
    T                   Thread;
};

template <typename T>
thread_local std::atomic_bool *interruptible<T>::LocalInterrupted = nullptr;

main.cpp

#include <iostream>
#include <unistd.h>
#include <thread>

#include "interruptible.hpp"

void DoStuff()
{
    try
    {
        while (true)
        {
            std::cout << "Loop" << std::endl;

            sleep(1);

            interruptible<std::thread>::check();
        }
    }
    catch (interrupted_exception const &e)
    {
        std::cout << "Interrupted!" << std::endl;
    }
}


int main()
{
    interruptible<std::thread> a(DoStuff);

    sleep(2);

    std::cout << "Interrupting..." << std::endl;
    a.interrupt();

    sleep(2);

    a->join();

    return 0;
}

当我用 g++ -std=c++11 main.cpp (gcc 4.9.2) 编译它时,我得到:

/usr/include/c++/4.9.2/functional:1665:61: error: no type named ‘type’ in ‘class std::result_of<interruptible<T>::interruptible(F&&, A&& ...) [with F = void (&)(); A = {}; T = std::thread]::<lambda(std::atomic_bool*, void (&)())>(std::atomic_bool*, void (*)())>’
       typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                             ^
/usr/include/c++/4.9.2/functional:1695:9: error: no type named ‘type’ in ‘class std::result_of<interruptible<T>::interruptible(F&&, A&& ...) [with F = void (&)(); A = {}; T = std::thread]::<lambda(std::atomic_bool*, void (&)())>(std::atomic_bool*, void (*)())>’
         _M_invoke(_Index_tuple<_Indices...>)
         ^

任何人都可以阐明这个问题,我们将不胜感激!

最佳答案

std::thread存储传递给其构造函数的参数的腐烂拷贝。对函数的引用会经历函数到指针的衰减,这意味着 DoStuff传递给 lambda 的实际类型参数是 void(*)() ,而其 operator()仍然期望衰减发生之前类型的参数,即 void(&)() (推导F)。对于 A 中的任何其他参数都会发生同样的问题。一旦您尝试使用它们,就立即参数包。

传递包裹在 std::ref 中的对象在这种情况下不是一个选项,因为引用包装器可能比存储它的实例的生命周期长(您实际上不知道 ArgumentsFunction 是什么,这些可能是纯右值临时对象,其生命周期在调用interruptible 的构造函数)。有人决定将某些实例包装在 std::ref 中可能是合理的。 在调用构造函数时 interruptible ,当人们知道某个对象的生命周期时,但这里不是这种情况。

如果您想最大限度地减少从 interruptible 传输参数所需的拷贝和移动次数到 lambda 表达式,然后进一步到 Function对象 - 一个选项是重写构造函数,如下所示:

#include <type_traits>
// ...
template <typename F, typename... A>
interruptible(F&& Function, A&&... Arguments) :
    Interrupted(false),
    Thread(
        [](std::atomic_bool *Interrupted,
           typename std::decay<F>::type&& Function,
           typename std::decay<A>::type&&... Arguments)
        {
            LocalInterrupted = Interrupted;
            Function(std::move(Arguments)...);
        },
        &this->Interrupted,
        std::forward<F>(Function),
        std::forward<A>(Arguments)...
    )
{ }

关于c++ - 如何将函数和参数包转发到 lambda、std::thread 的构造函数中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27751967/

相关文章:

c++ - 如何将 C++11 代码编译为 webassembly?

c++ - 正在释放的指针未分配,即使它之前已分配

c++ - Visual C++ 2008 Express 下载链接失效?

c++ - 是否有可复制但不可 move 的类的用例?

c++ - 如何在 while 循环中重新初始化 vector ?

c++ - 是否需要 std::launch::async 策略?

java - 客户端如何知道连接到哪个套接字?

java - 如何在ThreadFactory中初始化ThreadLocal变量?

java - 如果在静态初始化程序 block 中创建线程,程序将挂起

c++ - 自定义转换算子模板和内置算子 : no match for operator