可中断的Thread类C ++ 11 - 获取错误?

时间:2017-08-09 20:19:56

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

我遵循这里的想法https://stackoverflow.com/a/13893381/1324674试图创建一个线程类,该线程类将具有一个指向线程退出(或者更确切地说,抛出异常并结束)的布尔标志的静态指针。我有几个问题,但是在遵循相同的代码并且我想知道如何修复它时,我已经在这几个小时了 -

using namespace std;

class InterruptThreadException {};

class InterruptibleThread {
private:
    static thread_local atomic_bool* flagRef;
    atomic_bool flag{false};
    thread thrd;

public:
    friend void checkForInterrupt( );
    template < typename Function, typename... Args >
    InterruptibleThread( Function&& _fxn, Args&&... _args )
        : thrd(
                []( atomic_bool& f, Function&& fxn, Args&&... args ) {
                    flagRef = &f;
                    fxn( forward< Args >( args )... );
                },
                flag,
                forward< Function >( _fxn ),
                forward< Args >( _args )...

                ) {
        thrd.detach( );
    }
    bool stopping( ) const {
        return flag.load( );
    }

    void stop( ) {
        flag.store( true );
    }
};

thread_local atomic_bool* InterruptibleThread::flagRef = nullptr;

void checkForInterrupt( ) {
    if ( !InterruptibleThread::flagRef ) {
        return;
    }
    if ( !InterruptibleThread::flagRef->load( ) ) {
        return;
    }
    throw InterruptThreadException( );
}

void doWork( ) {
    int i = 0;
    while ( true ) {
        cout << "Checking for interrupt: " << i++ << endl;
        checkForInterrupt( );
        this_thread::sleep_for( chrono::seconds( 1 ) );
    }
}

int main( ) {
    InterruptibleThread t( doWork );
    cout << "Press enter to stop\n";
    getchar( );
    t.stop( );

    cout << "Press enter to exit" << endl;
    getchar( );
    return 0;
}

现在我相信这个问题来自于线程调用中的lambda函数,好像我删除了它,只是让fxn和args调用程序运行正常(显然没有终止线程的能力)。

知道我做错了吗?

以下错误 - visual studio:

Error   C2672   'std::invoke': no matching overloaded function found    
Error   C2893   Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...) noexcept(<expr>)'   

GNU: https://gist.github.com/anonymous/2d5ccfa9ab04320902f51d005a96d2ae

1 个答案:

答案 0 :(得分:4)

您的问题是std::thread存储了其参数的副本,因此Function的类型和传递给lambda的对象类型不相同。

当你致电InterruptibleThread(doWork)时,Function被推断为void (&)():也就是说,引用一个不带参数的函数并返回void。当您将fxn传递给std::thread的构造函数时,它会衰减到void (*)()(指向函数的指针,不带参数并返回void)并存储在某个内部存储中。当新线程启动时,内部对象作为第二个参数传递给lambda,但类型void (*)()与预期的void (&)()不匹配,一切都崩溃了。同样地,如果我将一个左值引用传递给某个仿函数,那么你的构造函数当前不起作用。

args存在类似的问题。例如,如果您将左值std::string作为args之一传递,它将作为std::string&&传递给lambda,并且会爆炸。

解决此问题的最简单方法是使lambda参数类型为auto(假设您正在使用c ++ 14):

InterruptibleThread( Function&& fxn, Args&&... args )
    : thrd(
            []( atomic_bool& f, auto&& fxn, auto&&... args ) {
                flagRef = &f;
                fxn( std::move(args)... );
            },
            std::ref(flag),
            std::forward<Function>(fxn),
            std::forward<Args>(args)... ) {
}

如果您仍然使用c ++ 11,则必须使用std::decay来获取正确的参数类型:

InterruptibleThread( Function&& fxn, Args&&... args )
    : thrd(
            []( atomic_bool& f,
                typename std::decay<Function>::type&& fxn,
                typename std::decay<Args>::type&&... args ) {
                flagRef = &f;
                fxn( std::move(args)... );
            },
            std::ref(flag),
            std::forward<Function>(fxn),
            std::forward<Args>(args)... ) {
}

请注意,在flag中我std::reference_wrapper包裹了std::atomic,因为template <typename T> class Tab { public: Tab(std::size_t length) : tab(length) {} // ... private: std::vector<T> tab; }; boost::variant<Tab<int>, Tab<double> > CreateTab(const std::string& type, std::size_t length) { if (type == "int") { return Tab<int>(length); } else if (type == "double") { return Tab<double>(length); } throw std::runtime_error("Bad type"); } 不可复制,即使是复制也不正确。