当多线程下的析构遇上虚函数

    技术2022-05-11  165

    今天遇到的一个教训: 有这样的一个基类 class service{ public:     void start();     void stop();     virtual void svc();  ...     virtual ~service(); }; service::~service(){ stop();} service是一个服务线程的基类,svc是线程的入口。在析构函数中调用stop确保线程优雅终止。有如下派生类: class some_service : service{ public:     virtual void svc();         //没有显式定义析构函数 }; 结果,程序崩溃!调试结果显示,stop过程导致线程崩溃,而这个stop是在析构函数中触发的。stop的工作过程大致是:通知service退出,等待service退出,清理数据。 过程很简单,不应该有什么错误。 再查看崩溃部分的代码,发现,svc中请求一个锁对象的时候失败,失败的原因是锁句柄无效!嗯,这大概是资源管理方面的漏洞了,仔细分析代码,问题就出在服务的析构函数身上!因为svc使用了some_service的成员数据,那么,按道理,svc这个线程,必须在对象析构以前安全退出,也就是说,在~some_service()调用时就要退出。~some_service()退出前,会析构所有的成员对象。如果此时的svc还没有退出,就会导致错误。不太严格的来说,一个正常的析构过程是:派生类的析构函数-->数据成员-->基类的析构函数-->基类的数据成员。很显然,stop的调用直到~service中才会被调用,而此时some_service::svc所需要的数据对象都已经被销毁了,当然会出问题。 在回头来看,这次的错误似乎是在some_service,没有恰当的stop线程,但是根源却在于service提供了一个易错的功能,这就是好心办坏事的典型! 于是, 修改~service为{assert(!running());} 那么要不要在~some_service 中实现{if (running()) stop();}呢?如果实现,那么,some_service的派生类仍然会遇到同样问题。如果不提供这样的实现,那么,用户必须在所有必要的地方调用stop(); 到目前为止,没有想到什么好办法来解决这一问题,最安全的办法就是让用户在每个必要的地方,调用stop(); 总结如下:在多线程的情况下,析构函数的效果不应该对线程执行函数造成任何影响。

    最新回复(0)