这是一个在我的应用程序中重现问题的相对简单的程序:
#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在worker`s thread中调用worker析构函数,因此崩溃了。我可以这样解决:
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好多少。有什么方法可以避免吗?
答案 0 :(得分:1)
thread
对象应该由管理线程生命周期的代码所拥有。
目前,您的thread
生命周期由您的worker
类管理,您的worker
类的生命周期由执行线程管理。
这是一个非常基本的管理循环。
工人破坏的语义也非常混乱。你有共享指针,当最后一个共享指针被销毁时,某个线程上的操作阻塞完成了一些未知的任务。在该系统下,预测哪些参考计数操作几乎不可能。
一种方法是捕捉工人内部工人的弱点。然后负责管理工作线程生命周期的代码在它们全部结束时进行选择;当共享指针消失时。
线程通常需要由外部实体管理。
可能有用的方法是具有命名任务队列的线程池。每个任务队列都保证按顺序运行,因此它就像一个线程,但是不能保证队列中的任务将在同一个线程上运行,也不保证线程将空闲等待队列中的新任务。
您可以使用类似guid的命名来命名队列,也可以使用根据请求生成的全局唯一标识符(如某种类型的new
'd指针)。在第二种情况下,等同于.join()
调用正在处理您的线程队列ID。
这确实可以让您远离使用原始boost
线程原语;但根据我的经验,std
- 样式原语是使用pthreads样式线程的一个步骤,但仍然远不是你真正想在客户端代码中直接使用的。