安全迭代器和非安全迭代器
通过上一篇博客的分析,我们已经大概知道 字典迭代器 的定义和用处了,也了解到安全迭代器和非安全迭代器的定义上的区别,还有安全迭代器的作用:给dict设置iterators++(dict里面的变量),这样字典的各种操作就不会执行单步rehash操作。
我们接下来就来讨论一下:为什么设置 iterators++ 之后就不能对字典进行rehash操作呢??
还有就是 安全迭代器和非安全迭代器的应用场景分别是怎样的??
一、iterators 变量与单步rehash
1.1 iterators 变量的定义
我们首先来看一下 iterators 变量是在哪个结构体中定义的:
* 字典
typedef struct dict {
// 类型特定函数
dictType *type;
// 私有数据
void *privdata;
// 哈希表
dictht ht[2];
// rehash 索引
// 当 rehash 不在进行时,值为 -1
int rehashidx; /* rehashing not in progress if rehashidx == -1 */
// 目前正在运行的安全迭代器的数量 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
int iterators; /* number of iterators currently running */
} dict;
可以看到,这个 iterators 变量 是在定义字典的时候用到的变量,也就是说, iterators 变量是字典的一个属性,这个iterators值表示目前正在运行的安全迭代器的数量,所以每当dictNext(dictIterator *iter)函数中调用到一次安全迭代器,这个值就会自增加一。
1.2 单步rehash
那这个 iterators值 和单步rehash又有什么关系呢,我们来看一下单步rehash的函数定义:
/*
* 在字典不存在安全迭代器的情况下,对字典进行单步 rehash 。
*
* 字典有安全迭代器的情况下不能进行 rehash ,
* 因为两种不同的迭代和修改操作可能会弄乱字典。
*
* 这个函数被多个通用的查找、更新操作调用,
* 它可以让字典在被使用的同时进行 rehash 。
*
* T = O(1)
*/
static void _dictRehashStep(dict *d) {
if (d->iterators == 0) dictRehash(d,1); //iterators == 0表示字典不存在安全迭代器
}
从上面的代码和注释,我们可以看到,只有当 iterators == 0,表示字典不存在安全迭代器时,才能调用 dictRehash() 函数来对字典进行rehash。
那什么时候会调用到这个 _dictRehashStep 函数呢,看下面的代码:
// 如果条件允许的话,进行单步 rehash
if (dictIsRehashing(d)) _dictRehashStep(d); !!!!!!!!!!!!
.......
#define dictIsRehashing(ht) ((ht)->rehashidx != -1)
(上面的代码是随便截取下来的,没有前后的逻辑性,只是为了说明问题而已)
从上面的代码块我们可以看到,当字典中的 rehashidx 参数不等于 -1 ,字典正在进行 rehash时,才能调用 _dictRehashStep 函数进行单步rehash操作。
那为啥调用dictRehash(d,1)函数之后就能执行所谓的“单步rehash”了呢,我们下面来看一下dictRehash() 函数的定义:
1.3 dictRehash() 函数的定义
* 执行 N 步渐进式 rehash 。
*
* 返回 1 表示仍有键需要从 0 号哈希表移动到 1 号哈希表,
* 返回 0 则表示所有键都已经迁移完毕。
*
* 注意,每步 rehash 都是以一个哈希表索引(桶)作为单位的,!!!!!!!!!!!!!
* 一个桶里可能会有多个节点,
* 被 rehash 的桶里的所有节点都会被移动到新哈希表。
*
* T = O(N)
*/
int dictRehash(dict *d, int n) {
// 只可以在 rehash 进行中时执行
if (!dictIsRehashing(d)) return 0;
// 进行 N 步迁移
// T = O(N)
while(n--) {
dictEntry *de, *nextde;
// 如果 0 号哈希表为空,那么表示 rehash 执行完毕
// T = O(1)
if (d->ht[