在设计并发系统的时候,常常有这么一个问题,例如我们有一个业务1,需要更新A,B,C三个对象,然后有另外一个业务2,要删除B对象。
这两个不同的业务,如果并发的话,很容易出现问题。例如在业务1进行到一半的时候,如果业务2突然把B删掉,那么可能导致业务1出现奇怪的异常。
引用计数可以用来解决这个问题。在B对象的结构体中,放入以下字段:
u32_t refcnt; // 引用计数
spinlock_t lock; // 保护计数和标志
u8_t flag; // 标志位,标志对象是否有效
void (*obj_final) (void *private);
void *private;
约定,只要对象的引用计数不为0,那么代表对象存活,且对象所在的拓扑是稳定的。例如,如果对象被放在一个红黑树里,只要对象的引用不为0,业务就可以放心地使用这个对象,并且认为对象始终在这个红黑树中。
在对象创建出来后,引用计数为1。在业务1更新B之前,需要获取B的指针,此时B的引用计数至少为1,因此也就可以保证B肯定不会被删除。
业务2在删除B对象的时候,同样首先要拿到B的指针,并将其减1。如果减1后,计数为0,那么说明没有其它业务在引用这个对象,将flag置为无效对象,然后做删除相关的业务(例如将对象从红黑树中移除)。如果引用计数不为0,说明当前有其它业务正在使用这个对象,这时业务2就设置好obj_final回调函数和private回调参数。
业务1使用完B对象后,将其计数减1。如果计数为0,那么将flag标志置为无效对象,代表对象B已经无效了。并且调用obj_final回调函数,obj_final会做业务2真正想做的删除业务,此时已经没有业务可以再访问到对象B了,对象B可以被安全删除。
简单的思路就是这样。