Pass parameters to std::thread wrapper

后端 未结 4 1423
一个人的身影
一个人的身影 2021-02-09 09:26

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 functio

相关标签:
4条回答
  • 2021-02-09 10:04

    In the error message, you can see the difference void (*)() vs void (&)(). That's because std::thread's constructor parameters are std::decayed.

    Add also std::ref to f:

    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)...)
    {
    }
    
    0 讨论(0)
  • 2021-02-09 10:07

    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. Indeed, compiling with -fsanitize=address is typically sufficient to demonstrate the problem.

    The answer by O'Neill does not work when e.g. 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.

    0 讨论(0)
  • 2021-02-09 10:10

    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.

    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.

    0 讨论(0)
  • 2021-02-09 10:17

    O'Neil and DeiDei got here first, and they're correct as far as I can tell. 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.

    Also note, that the underlying std::thread is not joined or detached properly before destruction, hence leading to std::terminate() being called.

    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;
    }
    
    0 讨论(0)
提交回复
热议问题