简体   繁体   English

将参数传递给std :: thread包装器

[英]Pass parameters to std::thread wrapper

I want to implement a small thread wrapper that provides the information if a thread is still active, or if the thread has finished its work. 我想实现一个小线程包装器,如果一个线程仍处于活动状态,或者该线程已完成其工作,它将提供信息。 For this I need to pass the function and its parameters to be executed by the thread class to another function. 为此,我需要将函数及其参数传递给线程类到另一个函数。 I have a simple implementation that should work but cannot get it to compile, and I can't figure out what to do to make it work. 我有一个简单的实现应该可以工作,但不能让它编译,我无法弄清楚该怎么做才能使它工作。

Here is my code: 这是我的代码:

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

class ManagedThread
{
public:
   template< class Function, class... Args> explicit ManagedThread( Function&& f, Args&&... args);
   bool isActive() const { return mActive; }
private:
   volatile bool  mActive;
   std::thread    mThread;
};

template< class Function, class... Args>
   void threadFunction( volatile bool& active_flag, Function&& f, Args&&... args)
{
   active_flag = true;
   f( args...);
   active_flag = false;
}

template< class Function, class... Args>
   ManagedThread::ManagedThread( Function&& f, Args&&... args):
      mActive( false),
      mThread( threadFunction< Function, Args...>, std::ref( mActive), f, args...)
{
}

static void func() { std::cout << "thread 1" << std::endl; }

int main() {
   ManagedThread  mt1( func);
   std::cout << "thread 1 active = " << std::boolalpha << mt1.isActive() << std::endl;
   ::sleep( 1);
   std::cout << "thread 1 active = " << std::boolalpha << mt1.isActive() << std::endl;

   return 0;
}

The compiler error I get: 我得到的编译器错误:

In file included from /usr/include/c++/5/thread:39:0,
                 from prog.cpp:4:
/usr/include/c++/5/functional: In instantiation of 'struct std::_Bind_simple<void (*(std::reference_wrapper<volatile bool>, void (*)()))(volatile bool&, void (&)())>':
/usr/include/c++/5/thread:137:59:   required from 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(volatile bool&, void (&)()); _Args = {std::reference_wrapper<volatile bool>, void (&)()}]'
prog.cpp:28:82:   required from 'ManagedThread::ManagedThread(Function&&, Args&& ...) [with Function = void (&)(); Args = {}]'
prog.cpp:35:28:   required from here
/usr/include/c++/5/functional:1505:61: error: no type named 'type' in 'class std::result_of<void (*(std::reference_wrapper<volatile bool>, void (*)()))(volatile bool&, void (&)())>'
       typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                             ^
/usr/include/c++/5/functional:1526:9: error: no type named 'type' in 'class std::result_of<void (*(std::reference_wrapper<volatile bool>, void (*)()))(volatile bool&, void (&)())>'
         _M_invoke(_Index_tuple<_Indices...>)
         ^

Live example is available here: https://ideone.com/jhBF1q 现场示例如下: https//ideone.com/jhBF1q

In the error message, you can see the difference void (*)() vs void (&)() . 在错误消息中,您可以看到差异void (*)() vs void (&)() That's because std::thread's constructor parameters are std::decay ed . 那是因为std :: thread的构造函数参数是std::decay ed

Add also std::ref to f : 添加std::reff

template< class Function, class... Args>
   ManagedThread::ManagedThread( Function&& f, Args&&... args):
      mActive( false),
      mThread( threadFunction< Function, Args...>, std::ref(mActive), std::ref(f), std::forward<Args>(args)...)
{
}

The answer by @O'Neil is correct, but I would like to offer a simple lambda approach, since you've tagged this as C++14 . @ O'Neil的答案是正确的,但我想提供一个简单的lambda方法,因为你已将其标记为C++14

template<class Function, class... Args>
ManagedThread::ManagedThread(Function&& f, Args&&... args):
      mActive(false),
      mThread([&] /*()*/ { // uncomment if C++11 compatibility needed
        mActive = true;
        std::forward<Function>(f)(std::forward<Args>(args)...);
        mActive = false;
      })
{}

This would eliminate the need for an external function all together. 这将不再需要外部功能。

O'Neil and DeiDei got here first, and they're correct as far as I can tell. O'Neil和DeiDei先来到这里,就我所知,他们是正确的。 However, I'm still posting my solution to your problem. 但是,我仍然在发布我的问题解决方案。

Here's something that would work better: 这里有更好的效果:

#include <atomic>
#include <thread>
#include <utility>

class ManagedThread {

public: /* Methods: */

    template <class F, class ... Args>
    explicit ManagedThread(F && f, Args && ... args)
        : m_thread(
            [func=std::forward<F>(f), flag=&m_active](Args && ... args)
                    noexcept(noexcept(f(std::forward<Args>(args)...)))
            {
                func(std::forward<Args>(args)...);
                flag->store(false, std::memory_order_release);
            },
            std::forward<Args>(args)...)
    {}

    bool isActive() const noexcept
    { return m_active.load(std::memory_order_acquire); }

private: /* Fields: */

    std::atomic<bool> m_active{true};
    std::thread m_thread;

};

It makes use of lambdas instead, and correctly uses std::atomic<bool> instead of volatile to synchronize the state, and also includes the appropriate noexcept() specifiers. 它使用lambdas代替,并且正确地使用std::atomic<bool>而不是volatile来同步状态,并且还包括相应的noexcept()说明符。

Also note, that the underlying std::thread is not joined or detached properly before destruction, hence leading to std::terminate() being called. 另请注意,底层std::thread在销毁之前未正确连接或分离,因此导致调用std::terminate()

I rewrote the test code as well: 我重写了测试代码:

#include <chrono>
#include <iostream>

int main() {
    ManagedThread mt1(
        []() noexcept
        { std::this_thread::sleep_for(std::chrono::milliseconds(500)); });
    std::cout << "thread 1 active = " << std::boolalpha << mt1.isActive()
              << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "thread 1 active = " << std::boolalpha << mt1.isActive()
              << std::endl;
}

The following is simple, elegant, and to my knowledge the most-correct of all current answers (see below): 以下是简单,优雅,据我所知,所有当前答案中最正确的(见下文):

template < class Function, class... Args >
ManagedThread::ManagedThread( Function&& fn, Args&&... args ) :
    mActive(false),
    mThread(
        [this]( auto&& fn2, auto&&... args2 ) -> void {
            mActive = true;
            fn2(std::forward<Args>(args2)...);
            mActive = false;
        },
        std::forward<Function>(fn), std::forward<Args>(args)...
    )
{}

The answer by DeiDei has a subtle but important flaw: the lambda takes the stack variables by reference, so if the thread starts, and tries to use, the stack variables after the constructor returns, you can get a stack-use-after-free error. DeiDei 的答案有一个微妙但重要的缺陷:lambda通过引用获取堆栈变量,因此如果线程启动并尝试使用,构造函数返回后的堆栈变量,则可以获得堆栈使用后免费错误。 Indeed, compiling with -fsanitize=address is typically sufficient to demonstrate the problem. 实际上,使用-fsanitize=address编译通常足以证明问题。

The answer by O'Neill does not work when eg any arguments are lvalues, and is a bit clunky. 奥尼尔的答案在例如任何参数都是左值时都不起作用,并且有点笨拙。

The answer by jotik is close, but does not work when the arguments are lvalues either, nor if they are member functions. jotik 的答案很接近,但是当参数是左值时,它们也不起作用,也不是成员函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM