Dies ist ein relativ einfaches Programm, das Problem in meiner Anwendung reproduzieren:Wie vermeidet man es, shared_ptr im unangemessenen Thread-Kontext zu zerstören?
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/noncopyable.hpp>
#include <boost/thread.hpp>
#include <chrono>
#include <functional>
#include <iostream>
#include <memory>
class worker : boost::noncopyable {
public:
explicit worker(boost::asio::io_service& io);
~worker();
void just_do_it(const std::function<void()>& when_done);
private:
boost::asio::io_service& io_;
boost::asio::io_service worker_io_;
boost::thread thread_;
};
worker::worker(boost::asio::io_service& io)
: io_(io)
{
thread_ = boost::thread([this] {
boost::asio::io_service::work my_work(worker_io_);
worker_io_.run();
});
}
worker::~worker()
{
worker_io_.stop();
std::clog << "join...\n";
thread_.join();
}
void worker::just_do_it(const std::function<void()>& when_done)
{
worker_io_.post([this, when_done] {
io_.post(when_done);
boost::asio::steady_timer(worker_io_, std::chrono::seconds(1)).wait();
});
}
int main()
{
boost::asio::io_service io;
boost::asio::steady_timer timer(io, std::chrono::seconds(5));
timer.async_wait(std::bind([] { std::clog << "terminating...\n"; }));
{
auto my_worker = std::make_shared<worker>(io);
my_worker->just_do_it([my_worker] {
std::clog << "did it\n";
my_worker->just_do_it([my_worker] {
std::clog << "did it second time\n";
// now my_worker is not needed and we allow it to die
});
});
}
io.run();
}
Wenn ich es auf Linux-Rechner laufen sehe ich:
did it
did it second time
join...
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::thread_resource_error> >'
what(): boost thread: trying joining itself: Resource deadlock avoided
Aborted
es wegen Shared_ptr stürzt ruft Arbeiter destructor in Beschäftigter s Faden. Ich kann es so reparieren:
std::shared_ptr<worker> holder;
{
holder = std::make_shared<worker>(io);
holder->just_do_it([&holder] {
std::clog << "did it\n";
holder->just_do_it([&holder] {
std::clog << "did it second time\n";
// now worker is not needed and we destroy it
holder.reset();
});
});
}
Aber es ist ein manuelles Objekt Lifetime Management. Es ist nicht zu viel besser als mit neuen und löschen. Gibt es eine Möglichkeit, es zu vermeiden?
Sie möchten dem Thread später trotzdem beitreten, oder? Du brauchst also einen Griff zum Faden außerhalb davon. –
Der Thread kann die Variablen der Arbeiterklassenmitglieder verwenden, deshalb muss ich ihn vor der Zerstörung des Arbeiters stoppen. –
Ihre Lösung ist nicht weniger automatisch als Ihr problematischer Code, Sie haben sich einfach geändert, wenn der Zeiger zerstört wird (und ist immer noch besser als das manuelle 'neue' /' löschen' in Bezug auf Ausnahmen); Sie können eine andere Lösung finden, aber die, die Sie zur Verfügung stellen, scheint die einfachste IMO zu sein. – piwi