这里是有原因的,先来看看大致的流程结构
fh 哈希表(链表解决散列冲突)
foo 哈希表节点(指向对象)
foo_alloc 申请节点
1、申请节点内存
2、插入到哈希表
2.1、哈希表加锁(hashlock)
2.2、插入新节点
2.3、节点加锁(此时节点已经添加到哈希表中了)
2.4、哈希表解锁
2.5、其他初始化动作
2.6、节点解锁
foo_hold 拿住节点
1、节点加锁(所有节点都应该是通过foo_alloc获取的,所以使用的时候已经在哈希表中了)
2、节点引用计数加1
2、节点解锁
foo_find 查找节点
1、哈希表加锁(避免此时有申请或者释放节点)
2、查找节点
3、哈希表解锁
foo_rele 释放节点
1、节点加锁
2、判断节点引用计数是否为1
2.1、节点引用计数为1,做以下动作
a. 节点解锁,哈希表加锁
b. 节点加锁
判断此时节点引用计数是否为1,不为1则将引用计数减1
然后解锁节点、解锁哈希表,函数返回
c. 从哈希表中移除节点
d. 哈希表解锁
e. 节点解锁
f. 释放节点内存(需要释放锁)
2.2、节点引用计数不为1
节点引用计数减1
节点解锁
几个函数大致就是这样的结构,前面的就不画图了,最后foo_rele
画一个图详细的说一下。
如果在第一次判断引用计数为1的时候,不对节点进行解锁,直接从哈希表中摘除并进行释放,这里会出问题。
因为你需要摘除节点,就必须对哈希表进行加锁,如果没有加锁,则可以有一个线程调用了foo_find
拿到了这个节点。又因为释放节点必须先对其解锁,那么就有可能你这里刚解锁,另一个线程通过foo_hold
对其进行使用了。然后节点被销毁,可是还有一个线程正在引用它。
线程A: 节点加锁---> --->节点解锁 ---->节点销毁
线程B: foo_find拿到节点 -->foo_hold使用节点 -->引用节点出错
添加节点的时候,节点都是新申请的,不存在被其他线程使用的情况,所以不会造成死锁。