可中断线程类C ++ 11-遇到错误? [英] Interruptible Thread class C++11 - getting errors?

查看:78
本文介绍了可中断线程类C ++ 11-遇到错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在此处 https://stackoverflow.com/a/13893381/1324674 尝试创建一个线程类,该类具有指向布尔标志的静态指针,该布尔标志将告诉线程退出(或者抛出异常并结束).我遇到了几个问题,但是在遵循相同的代码时,我想知道如何解决它,我已经为此工作了几个小时-

I'm following the idea here https://stackoverflow.com/a/13893381/1324674 attempting to create a thread class that will have a static pointer to a boolean flag that will tell a thread to quit (or rather, throw an exception and end). I'm having several issues however while following that same code and I'm wondering how to fix it, I've been working on this for several hours now-

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调用一样,程序运行正常(显然无法终止线程).

Now I believe the issue is coming from the lambda function inside of the thread call as if I remove that and just have the fxn and args call the program runs fine (obviously without the ability to terminate a thread).

知道我在做什么错吗?

Visual Studio以下错误:

Errors below- 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

推荐答案

您的问题是std::thread存储其参数的副本,因此Function的类型和传递给您的lambda的对象的类型都不是.一样.

Your issue is that std::thread stores a copy of its arguments, so the type of Function and the type of the object passed to your lambda aren't the same.

当您调用InterruptibleThread(doWork)时,推导出Functionvoid (&)():即,引用不带参数且返回void的函数.当您将fxn传递给std::thread的构造函数时,它会衰减为void (*)()(指向不带参数并返回void的函数的指针),并存储在某些内部存储器中.当新线程启动时,内部对象将作为第二个参数传递给您的lambda,但是类型void (*)()与预期的void (&)()不匹配,并且一切都崩溃了.同样,如果我向其传递某个函子的左值引用,则您的构造函数当前将无法正常工作.

When you call InterruptibleThread(doWork), Function gets deduced to be void (&)(): that is, reference to a function taking no arguments and returning void. When you pass fxn along to std::thread's constructor, it decays to void (*)() (pointer to a function taking no arguments and returning void) and gets stored in some internal storage. When the new thread starts up that internal object gets passed as the second parameter to your lambda, but the type void (*)() doesn't match the expected void (&)() and everything falls apart. Similarly your constructor currently doesn't work if I pass it an lvalue reference to some functor.

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

A similar problem exists for args. For example, if you pass an lvalue std::string as one of args, it will get passed to the lambda as a std::string&& and things will explode.

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

The easiest way to fix this is to make the lambda parameter type auto (assuming you're using 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来获取正确的参数类型:

If you're still on c++11, you'll have to use std::decay to get the correct parameter type:

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是不可复制的,即使进行复制也是不正确的.

Note in both cases I've wrapped flag in a std::reference_wrapper since std::atomic is not copyable and it would be incorrect to make a copy even if it were.

这篇关于可中断线程类C ++ 11-遇到错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆