почему происходит сбой, когда член класса содержит "std :: shared_ptr«? - PullRequest
0 голосов
/ 28 ноября 2018

Я обнаружил, что мое приложение зависнет, когда член класса содержит "std :: shared_ptr".Например:

#include <thread>
#include <memory>

class Derived {
public:
    Derived() {
        mThread = std::make_shared<std::thread>(&Derived::callback, this);
    }
    ~Derived() { }

    void callback() {}

    int add(int x, int y) { return x + y; }

private:
    std::shared_ptr<std::thread> mThread;
};

int main() {
    Derived * base = new Derived();
    delete base;
    return 0
}

Хотелось бы узнать почему?

Ответы [ 3 ]

0 голосов
/ 28 ноября 2018

Я думаю, что у вас есть недостаток дизайна, так как вы используете shared_ptr для потока без гарантии владения Derived.

Как указано snake_style, вы должны присоединиться к вашему потоку.

I'mЯ буду утверждать, что вы должны хранить поток по значению в вашем классе, чтобы гарантировать время жизни Derived:

#include <thread>
#include <memory>

class Derived {
 public:
    Derived() {
        mThread = std::thread(&Derived::callback, this);
}
~Derived() { 
        if (mThread.joinable())
              mThread.join();
}

    void callback() {}

    int add(int x, int y) { return x + y; }

private:
    std::thread mThread;
};

Оператор if в Dtor защищает вас от неправильного поведения при перемещении Derived.

Другой способ гарантировать владение - поместить Derived в поток:

 thread = std::thread([d = Derived{}]() mutable { d.callback(); };

Это перемещает переменную потока из класса.

Третьим решением может быть помещение Derived в shared_ptr и использованиеконструктор псевдонимов shared_ptr для создания общего указателя потоков.Я не работаю с этим, так как это может быть слишком сложно, и вы также можете поместить shared_ptr в качестве захвата в вашу лямбду.

0 голосов
/ 28 ноября 2018

Если вы хотите сохранить поток в shared_ptr, было бы лучше создать фабричную функцию с пользовательским средством удаления shared_ptr, которое присоединяется к потоку.

#include <thread>
#include <memory>
#include <iostream>

template<typename... Args>
std::shared_ptr<std::thread> makeThread(Args&&... args)
{
    return std::shared_ptr<std::thread>(new std::thread(std::forward<Args>(args)...)
                                        , [](std::thread* t) { t->join(); delete t; });
}

void test(std::int32_t &v)
{
    for (int workload = 0; workload < 64; ++workload)
        ++v;
}

int main(int argc, char *argv[])
{  
    std::int32_t v = 0;
    auto sptr_thread = makeThread(test, std::ref(v));
    std::cout << "value before exiting: " << v << std::endl;

    return 0;
}

В отличие от конструкторов std :: shared_ptr, std:: make_shared не разрешает пользовательское удаление.Я бы предпочел фабрику, так как невозможно забыть присоединиться к ветке

0 голосов
/ 28 ноября 2018

Когда вы запускаете потоки, вы должны присоединиться или отсоединить их до вызова деструктора потока.Я предлагаю сделать это в деструкторе:

#include <thread>
#include <memory>

class Derived
{
public:
    Derived()
    {
        mThread = std::make_shared<std::thread>(&Derived::callback, this);
    }
    ~Derived()
    {
        if(mThread->joinable())
            mThread->join();
    }
    void callback() {}
    int add(int x, int y) { return x + y; }
private:
    std::shared_ptr<std::thread> mThread;
};

int main()
{
    Derived * base = new Derived();
    delete base;
    return 0;
}

Кстати, использование shared_ptr в вашем примере не является необходимым.Вы можете просто определить thread variable:

thread mThread;

Начать новую тему:

mThread = std::thread(&Derived::callback, this);

И присоединиться к ней, когда вам нужно:

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