Обтекание std :: thread для повторного использования в качестве члена класса - PullRequest
0 голосов
/ 15 октября 2018

Я хочу, чтобы мой класс порождал два потока, которые будут работать в цикле и взаимодействовать с классом владельца.Это было легко, когда у меня сначала был один класс с одним потоком: один атомарный флаг, один мьютекс и т. Д. Но поскольку требования ко второму потоку повысились, я думаю о более элегантном решении, которое будет инкапсулировать поток и его утилиты.Например: класс ThreadWrapper, который может быть создан в классе владельца.

До сих пор я придумал следующее, однако я не уверен, что это правильный путь:

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->threadLoop(); }));
    }

    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;
};

Использование (исправлено):

class Foo
{

    Foo()
    : t1(std::function<void()>(std::bind(&Foo::internalLoop1, this)))
    , t2(std::function<void()>(std::bind(&Foo::internalLoop2, this)))
    {
    }

    ThreadWrapper t1;
    ThreadWrapper t2;

    std::mutex mtx;

    void internalLoop1()
    {
        // looped task
        {
            std::scoped_lock lock(mtx);
            // write'n'read data to tx/rx queues
        }
    }

    void internalLoop2()
    {
        // looped task            
        {
            std::scoped_lock lock(mtx);
            // process the rx queue
        }
    }
};

Это похоже на Wrapper, который я нашел здесь .Недостатком является то, что период ожидания должен быть передан объекту ThreadWrapper.

Это правильный подход?Или, может быть, это следует сделать с помощью базового класса и просто унаследованного виртуального метода?Как показано на рисунке ниже.Thread model from codeproj.

Источник: https://www.codeproject.com/Articles/18383/A-thread-wrapper-class

В идеале я хотел бы использовать шаблон здесь, однако единственная «другая» часть между ThreadWrapper - это один метод, что скорее подсказывает мне указатели наследования / функции.Или я здесь не прав?

РЕДАКТИРОВАТЬ: Мотивация.В C # можно сделать следующее: var t1 = new Thread(() => internalLoop1()); В конце я присматриваю за такой строкой в ​​моем основном классе.Где другой класс ThreadWrapper позаботится об управлении безопасностью потока.Следовательно, легко реплицируемый, если нужно создать несколько потоков.

...