我的情况是退出软件时偶尔产生这种错误,也算是多线程中的一个使用不当的情况。
简化后的模型如下:
1 | class A |
2 | { |
3 | public : |
4 | virtual void vf() = 0; |
5 | void fa() { |
6 | vf(); |
7 | } |
8 | ~A() { |
9 | fa(); |
10 | } |
11 | private : |
12 | Thread * thread ; |
13 | }; |
14 |
15 | class AA : public A |
16 | { |
17 | virtual void vf() { /*do something*/ } |
18 | }; |
19 |
20 | int main() |
21 | { |
22 | AA *aa = new AA; |
23 | delete aa; |
24 | return 0; |
25 | } |
没错就是在基类的构造/析构函数中对虚函数进行了调用。实际的情况是:线程T1和T2持有同一个AA对象的指针,当T1在对AA进行析构,执行到A::~A()中时,T2还在跑着AA::vf(),因为一旦进入A::~A()后,对象的虚表指针已经被重新指向了基类A的虚表,不再是AA的了,然后在这个间隙里T2产生了对vf()的调用,就抛出异常了。
遇到这个情况,简单的做法是先在AA析构之前先让线程T2终止。然而软件将线程安置在基类中(如上面A::thread),要求派生类去执行停止线程动作,这样便会使得接口的易用性下降,因为很有能会忘记执行了,就跟忘记释放内存一样,在不知不觉中又引回了这个问题。
最后还是去掉了vf的纯虚属性,变成一个实现为空的普通虚函数了。