Как управлять ресурсом потока с полиморфизмом?(C ++) - PullRequest
0 голосов
/ 30 апреля 2019

Я пытаюсь реализовать полиморфизм, где производный класс реализует метод, который запускается в отдельном потоке:

#include <memory>
#include <thread>
#include <chrono>

class Base
{
public:
    std::thread m_jobThread;

    ~Base() { if (m_jobThread.joinable()) m_jobThread.join(); }

    virtual void doJob() = 0;
    void doJobInBackground() { m_jobThread = std::thread(&Base::doJob, this); }
};

class Drived : public Base
{
public:
    Drived() = default;
    virtual void doJob() final { std::this_thread::sleep_for(std::chrono::seconds(1)); }
};

int main(int argc, char const *argv[])
{
    Drived d;
    d.doJobInBackground();
    return 0;
}

Как мне достичь этого безопасно, не получив исключение pure virtual method called?

Я хочу иметь возможность уничтожать производные объекты, потенциально во время выполнения задания, и деструктор базового класса должен заниматься управлением потоком. но поскольку виртуальная таблица производного класса уничтожается до запуска деструктора базового класса, я получаю исключение чисто виртуального метода.

Я думал о добавлении метода stop в деструктор производного класса, который обеспечивает соединение потока. Но это противоречит цели моего полиморфного дизайна, когда я хочу, чтобы производный класс отвечал ТОЛЬКО за определение метода doJob, а не за прямую или косвенную обработку ресурсов базового класса, таких как поток ...

Есть идеи, если это возможно? Нужно ли менять свой дизайн?

1 Ответ

1 голос
/ 30 апреля 2019

Как отметил Сэм Варшавчик в своем комментарии выше, причина, по которой вы запускаете здесь вызов чисто виртуальной функции, заключается в том, что деструктор d запускается еще до того, как ваш второй поток даже начал выполняться.Вы синхронизируете только в деструкторе Base.Однако к моменту запуска деструктора Base часть объекта Derived уже уничтожена.Динамический тип объекта на данный момент просто Base, потому что это все, что еще живо, и, таким образом, вызов виртуальной функции отправляется в базовую версию, которая является чистой.Строго говоря, у вас на самом деле здесь неопределенное поведение, потому что объект, уничтожаемый в одном потоке, в то время как другой может вызывать метод для него, нарушает [basic.life] /7.2.Время жизни вашего объекта заканчивается, когда начинается вызов деструктора [basic.life] /1.3, вызов метода во втором потоке не inter-thread происходит до вызов деструктора в первом потоке ...

Просто

#include <thread>
#include <chrono>

class Base
{
    std::thread m_jobThread;

public:
    void join() { if (m_jobThread.joinable()) m_jobThread.join(); }

    virtual void doJob() = 0;
    void doJobInBackground() { join(); m_jobThread = std::thread(&Base::doJob, this); }
};

class Derived : public Base
{
public:
    virtual void doJob() final { std::this_thread::sleep_for(std::chrono::seconds(1)); }
};

int main(int argc, char const* argv[])
{
    Derived d;
    d.doJobInBackground();
    d.join();
    return 0;
}

отлично работает ...

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