Как дождаться создания объекта в другом потоке для вызова деструктора?
Необходимо иметь возможность создать объект в другом потоке и затем удалить в любое время (для асинхронного создания и перехода в машине состояний). Вызывается конструктор в другом потоке, в это время основной поток пытается вызвать деструктор, но до создания объект не удалятся. Этот код, к сожалению, не работает. Деструктор вовсе не вызывается и Visual Leak Detector находит утечку памяти:
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
class Ex
{
private:
std::mutex m_mutex;
std::string m_str;
public:
Ex(const std::string& str) :
m_str(str)
{
std::lock_guard lock(m_mutex);
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Ex constructed!\n";
}
const std::string& get() const
{
return m_str;
}
~Ex()
{
std::lock_guard lock(m_mutex);
std::cout << "Ex destructed!" << " m_str = " << m_str << '\n';
}
};
int main()
{
{
std::unique_ptr<Ex> ptr;
std::thread thread([&]
{
ptr = std::make_unique<Ex>("string");
});
thread.detach();
}
std::getchar();
}
Ответы (3 шт):
Спасибо за помощь! Было наивно полагать, что мьютекс объекта умного указателя будет распространятся на сам умный указатель. Если я правильно понял, std::shared_ptr использует атомарный подсчёт ссылок и будет правильно удалён в зависимости от того, какой поток последним вызовет его деструктор:
std::shared_ptr<Ex> ptr;
std::thread thread([](std::shared_ptr<Ex> p)
{
p = std::make_shared<Ex>("string");
}, ptr);
thread.detach();
Теперь единственная ошибка, которая может произойти - при выходе из программы отсоединённый поток будет просто уничтожен и ресурсы не будут правильно освобождены.
PS: Если кто-то набрёл на этот ответ: он неверный.
Разве этот код:
std::shared_ptr<Ex> ptr = std::make_shared<Ex>("string");
stateMachine.append(ptr);
Не будет работать аналогичным образом с этим?
std::shared_ptr<Ex> ptr;
std::shared_ptr <std::atomic<bool>> flag = std::make_shared<std::atomic<bool>>(false);
std::thread thread([&](std::shared_ptr<Ex> p, std::shared_ptr <std::atomic<bool>>)
{
p = std::make_shared<Ex>("string");
flag->store(true);
}, ptr, flag);
thread.detach();
while (!flag->load())
{
}
stateMachine.append(ptr);
PS: я проверил - действительно не работает. Потому что я перезаписываю умный указатель. Спасибо за помощь - буду уже самостоятельно подробно изучать как всё это работает.
Теперь я наконец-то понял, зачем использовать std::future и возвращать значения из потока. Думаю, это идеальное решение, ведь при "внезапном" уничтожении срабатывает функция wait(), то есть главный поток гарантированно дождётся побочного. К тому же, нет возни с исключениями в потоке:
int main()
{
std::shared_ptr<Ex> ptr;
auto future = std::async(std::launch::async,
[]
{
return std::make_shared<Ex>("Hello world!");
});
ptr = future.get();
std::getchar();
}