上一节我们使用了一个智能指针的方法,来防止C++中由于申请的内存未释放引起的内存泄漏的问题。
但是,上一节的代码中还有一些问题,是什么问题呢?
在上一节的代码中,我们定义了一个基类RefBase,在RefBase中定义了一个引用计数count,还有一些列count加1和减1操作。
问题就出在这个加一操作和减一操作上面,它们并不是原子操作,即它们实际上是通过几个指令完成的。
以加一操作为例,count++ 实际上是分成三步完成的:
创建了一个sp类,那么对应的会有一个 count 计数值产生。
假设有A,B两个进程都会使用这个sp类,那么就可能出现下面的情况,初始的count值为1:
(所谓的A进程寄存器和B进程寄存器从物理上说是相同的寄存器,但是他们在不同时刻存放的值不同。)
内存中的count值在经过A,B两个进程的引用后,应该为3,但是此时结果却为2。
出现这样情况的原因就在于,count++的操作不是一个原子操作,一旦在操作的过程中被打断,且在打断过程中有别的进程对 count 值进行操作,那么就可能出现错误。
这种在多进程情况下可能出现问题的代码,也叫作非线程安全代码。
想要修改为线程安全代码,可以将count加一和减一变为原子操作。
安卓源码中就有类似操作。
其中__sync_fetch_and_add 和__sync_fetch_and_sub 就是不可以被打断的原子操作,并且__sync_fetch_and_sub 返回的是mCount减一之前的值。
修改代码,使用安卓源码中的 LightRefBase。
LightRefBase是一个模板。
将代码中的sp类删除,使用安卓源码中的sp类。
编译测试,程序正常执行。
需要注意一点是,在之前的代码中,我们在sp类的析构函数中,执行delete操作。
但是在安卓轻量级指针中,是在RefBase 中执行的 delete操作。
最后,需要说明一下安卓轻量级指针并不是完美的,它仅仅只是对count计数值做了原子操作,对于这个对象本身并不是线程安全的,因为delete操作并不是线程安全的。
本节代码比较简单,主要是认识和使用一下安卓轻量级指针。
本节代码可以通过git下载:
- git clone https://github.com/weidongshan/cpp_projects.git
- git checkout v20