lundi 15 octobre 2018

Wrapping std::thread for reuse as a class member

I want my class to spawn two threads, that will run in a loop and interact with the owner class. This was easy, when I had my class with single thread at first: one atomic flag, one mutex etc. But as the requirement for second thread rose, I am thinking of some more elegant solution, that will encapsulate the thread and its utilities. Such as: a ThreadWrapper class, that could be instantiated in the owner class.

So far, I came up with the following, however I am not certain if this is the right way to go:

class ThreadWrapper
{
public:
    ThreadWrapper()
        : m_threadShouldRun(false)
        , m_threadRunning(false)
    {
    }
    ThreadWrapper(std::function<void()> fn)
        : m_threadShouldRun(true)
        , m_threadRunning(false)
        , m_threadFunction(fn)
    {
        m_threadPointer = std::make_shared<std::thread>(std::thread([&] { this->treadLoop(); }));
    }

    virtual ~ThreadWrapper()
    {
        if (m_threadRunning)
        m_threadShouldRun = false;
        m_threadPointer->join();
        m_threadPointer = nullptr;
    }

private:
    void threadLoop()
    {
        m_threadRunning = true;

        while(m_threadShouldRun)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            m_threadFunction();
        }
        m_threadRunning = false;
    }

    std::function<void()> m_threadFunction = []{};
    std::shared_ptr<std::thread> m_threadPointer;
    std::atomic<bool> m_threadShouldRun;
    std::atomic<bool> m_threadRunning;
};

Usage:

class Foo
{

    Foo()
    : t1(this->internalLoop1)
    , t2(this->internalLoop2)
    {
    }

    ThreadWrapper t1;
    ThreadWrapper t2;

    std::mutex mtx;

    internalLoop1()
    {
        // looped task
        {
            std::scoped_lock lock(mutex);
        }
    }

    internalLoop2()
    {
        // looped task            
        {
            std::scoped_lock lock(mutex);
        }
    }
}

This is somewhat similar to a Wrapper that I found here. The shortcoming is that the sleep period needs to be passed to the ThreadWrapper object.

Is this a correct approach? Or maybe it should be done with a basis class and just inherited virtual method instead? As shown on the pic below. Thread model from codeproj.

Source: https://www.codeproject.com/Articles/18383/A-thread-wrapper-class

Ideally I would like to use a template here, however the only "different" part between both ThreadWrapper is a single method, which rather suggests me inheritance/function pointers. Or am I wrong here?

Aucun commentaire:

Enregistrer un commentaire