这是一个相对简单的程序,可重现我的应用程序中的问题:
#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();
}
当我在 Linux 机器上运行它时,我看到:
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
它崩溃是因为 shared_ptr 在工作线程中调用了工作析构函数。我可以这样修复它:
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();
});
});
}
但它是一个手动的对象生命周期管理。它并不比使用 new 和 delete 好太多。有什么办法可以避免吗?
最佳答案
thread
对象应该由管理线程生命周期的代码拥有。
目前,thread
的生命周期由 worker
类管理,worker
类的生命周期由执行线程管理.
这是一个非常基本的管理循环。
worker 销毁的语义也非常困惑。你有共享指针,当最后一个共享指针被销毁时,操作 block 在某个线程上某处完成一些未知任务。在这个系统下,预测什么引用计数操作阻塞变得几乎不可能。
一种方法是在 worker 中捕获指向 worker 的弱指针。然后负责管理工作线程生命周期的代码在它们全部结束时进行选择;当共享指针消失时。
线程通常需要由外部实体管理。
一种可能有用的方法是使用具有命名任务队列的线程池。每个任务队列都保证按顺序运行,所以它就像一个线程,但不能保证队列中的任务会在同一个线程上运行,也不能保证一个线程会空闲等待队列中的新任务。
您可以使用诸如 guid 之类的东西来命名队列,或者根据请求生成一个不太全局唯一的标识符(比如某种 new
指针)。在第二种情况下,您的 .join()
调用相当于处理您的线程队列 ID。
这确实使您远离使用原始 boost
线程原语;但根据我的经验,std
风格的原语比使用 pthreads 风格的线程更上一层楼,但距离您实际想要直接在客户端代码中使用的内容还差得很远。
关于c++ - 如何避免在不适当的线程上下文中破坏 shared_ptr?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36595162/