EventTask с std :: condition_variable - PullRequest
0 голосов
/ 02 июля 2019

Я пытаюсь создать EventTask, который вызывает функцию, переданную в цикле.

Мне нужно, чтобы он дождался запуска, а затем пометил его как завершенный.

Моя проблема в том, что я не знаю какчтобы получить аргументы от моей функции ожидания для передачи в функцию, которая называется

Как вы можете видеть, проблема в моем taskFunc _event.wait должен установить аргументы для передачи в функцию.

class Event
{
public:

    Event() : _signalled(false) {}

    virtual inline void notify(){
        std::unique_lock<std::mutex> lock(_mutex);
        _signalled = true;
        _condvar.notify_one();
    }

    virtual inline void wait(){
        std::unique_lock<std::mutex> lock(_mutex);
        _condvar.wait(lock, [&] { return _signalled; });
        _signalled = false;

        stop();
    }

    virtual inline void stop(){
        std::unique_lock<std::mutex> lock(_mutex);
        _signalled = false;
    }

private:

    std::mutex _mutex;
    std::condition_variable _condvar;
    bool _signalled;
};

template <class T>
class EventArg : public Event
{
public:

    virtual inline void notify(T arg){
        Event::notify();
        this->arg = arg;
    }

    virtual inline void wait(T& arg){
        Event::wait();
        arg = this->arg;
    }

private:
    T arg;
};

template<class... Args>
class EventTask
{
public:
    EventTask(std::function<void(Args...)> func) : m_Function(func), m_Run(true), m_thread(&taskFunc, this) {}

    void notify(Args&& ...Params) { 
        _Event.notify(std::forward<Args>(Params)...); }

    void wait() { 
        _EventFinished.wait(); }

    void stop() {
        m_stop = true;
        _Event.stop();
    }

private:
    void taskFunc()
    {
        void* pArg = nullptr;
        while (m_Run){
            _Event.wait(pArg);
            m_Function(std::forward<Args>(Params)...);
            _EventFinished.notify();
        }
    }

private:
    std::function<void(Args...)> m_Function;
    bool m_Run;
    std::thread m_thread;
    EventArg<Args...> _Event;
    Event _EventFinished;
};

1 Ответ

0 голосов
/ 02 июля 2019

Попробуйте это:

#include <iostream>
#include <functional>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <tuple>

template<class... Args>
class EventTask
{
public:
    EventTask(std::function<void(Args...)> func) : m_Function(func), m_Run(true) {
        m_thread = std::thread{ [=]() {
            taskFunc();
        }};
    }
    ~EventTask() {
        stop();
        m_thread.join();
    }

    void notify(const std::tuple<Args...> &args) {
        std::unique_lock<std::mutex> lock(_mutex);
        _signalled = true;
        _args = args;
        _condvar.notify_all();
    }

    void stop() {
        m_Run = false;
        _condvar.notify_all();
    }

private:
    void taskFunc()
    {
        std::tuple<Args...> args;
        while (true){
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _condvar.wait(lock, [&] { return m_Run && _signalled; });
                if (!m_Run) break;
                _signalled = false;
                args = _args;
            }
            std::apply(m_Function, args);
            //_EventFinished.notify();
        }
    }

private:
    std::function<void(Args...)> m_Function;

    std::tuple<Args...> _args;
    std::mutex _mutex;
    std::condition_variable _condvar;
    bool _signalled = false;

    //Event _EventFinished;
    bool m_Run;
    std::thread m_thread;
};

int main()
{
    EventTask<int, int> ma{ [](int a, int b) {

    }};
    ma.notify({ 1, 2 });
}

Что здесь происходит? Существует два потока: поток «производителя» (тот, который создает аргументы для функции, а следовательно, и имени) и поток «потребителя» (тот, который фактически выполняет).

Поток "продюсер" блокирует мьютекс, копирует аргументы и уведомляет, что нужно что-то сделать. «потребительский» поток блокирует мьютекс, затем ожидает при условии. Ожидание при условии (и мьютекс) освобождает мьютекс, который будет получен при получении уведомления о переменной условия. Когда переменная «продюсер» устанавливает аргументы, «потребитель» проснется, повторно получит мьютекс (это необходимо, в противном случае «производитель» может установить аргументы дважды подряд, приводя к гонке, что является неопределенным поведением), один раз снова копирует аргументы и освобождает мьютекс. Затем он продолжает вызывать рабочую функцию, используя собственную локальную копию аргументов.

Подобный процесс происходит, когда вы пытаетесь остановить все это. «продюсер» блокирует мьютекс, устанавливает m_Run в false и уведомляет всех. «потребительская» нить проснулась, уведомляет, что m_Run ложно и завершает цикл. Обратите внимание, что это не нарушит рабочую функцию, которая уже выполняется - вам нужно подождать (обратите внимание на вызов join в деструкторе), чтобы она завершилась.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...