转载请注明出处:http://blog.csdn.net/luotuo44/article/details/42913549
锁级别:
从前面的《扩展哈希表》知道:哈希表进行扩展时,有一个专门的线程负责将item数据从旧哈希表迁移到新哈希表(因此,也称这个线程为迁移线程)。此外,还有一些worker线程会时不时访问item(包括插入、删除和获取)。这些线程的对item所做的操作基本上都是互斥的,必须加锁控制。
如果只使用一个锁,抢到锁能使用哈希表,抢不到则不能使用。那么memcached的效率将变得相当低。为此,memcached采用类似数据库的策略:使用不同级别的锁。memcached定义了两个级别的锁:段级别和全局级别。在平时(不进行哈希表扩展时),使用段级别的锁。在扩展哈希表时,使用全局级别的锁。
段级别是什么级别?将哈希表按照几个桶一段几个桶一段地平均分,一个段对应有多个桶,每一个段对应有一个锁。所以整个哈希表有多个段级别锁。由于段级别锁的数量在程序的一开始就已经确定了,不会再变的了。而随着哈希表的扩展,桶的数量是会增加的。所以随着哈希表的扩展,越来越多的桶对应一个段,也就是说越来越多的桶对应一个锁。
在哈希表扩展阶段,迁移线程和workers线程都使用全局锁。这些线程竞争全局锁,抢到锁才允许对哈希表的item进行操作。在非扩展阶段,迁移线程处于休眠状态,workers线程使用段级别锁,抢到了某个段锁就允许访问对应的多个桶。因此如果不同的worker线程访问不同的段,那么就可以同时访问了,增加了并发量。
下面看一下段级别锁和全局级别锁的定义。thread_init函数分配并初始化段级别锁。
static pthread_mutex_t *item_locks;//指向段锁数组的指针
/* size of the item lock hash table */
static uint32_t item_lock_count;//段锁的数量
static unsigned int item_lock_hashpower;
static pthread_mutex_t item_global_lock;//全局锁
#define hashsize(n) ((unsigned long int)1<<(n))
void thread_init(int nthreads, struct event_base *main_base) {
int i;
int power;
pthread_mutex_init(&cache_lock, NULL);
pthread_mutex_init(&init_lock, NULL);
pthread_cond_init(&init_cond, NULL);
//nthreads是workers线程的数量,由main函数调用时传入来
if (nthreads < 3) {
power = 10;
} else if (nthreads < 4) {
power = 11;
} else if (nthreads < 5) {
power = 12;
} else {//最大为13
/* 8192 buckets, and central locks don't scale much past 5 threads */
power = 13;
}
//power是2的幂
item_lock_count = hashsize(power);
item_lock_hashpower = power;
//哈希表中段级别的锁。并不是一个桶就对应有一个锁。而是多个桶共用一个锁
item_locks = calloc(item_lock_count, sizeof(pthread_mutex_t));
if (! item_locks) {
perror("Can't allocate item locks");
exit(1);
}
for (i = 0; i < item_lock_count; i++) {
pthread_mutex_init(&item_locks[i], NULL);
}
pthread_mutex_init(&item_global_lock, NULL);
...
}
切换锁级别:
现在看一下怎么使用段级别锁和全局级别锁。迁移线程并不会使用段级别锁,在assoc.c的assoc_maintenance_thread函数中,迁移线程只会调用item_lock_global()函数锁上全局锁item_global_lock。这里主要是看workers线程是怎么使用段级别锁和全局级别锁的。
worker线程的锁级别:
workers线程如果要访问哈希表的item,会先调用item_lock函数进行加锁。item_lock函数会根据需要自动选择使用段级别锁还是全局级别锁。下面是具体的代码。
//memcached.h文件
//item锁级别
enum item_lock_types {
ITEM_LOCK_GRANULAR = 0, //段级别
ITEM_LOCK_GLOBAL //全局级别
};
//thread.c文件
static pthread_key_t item_lock_type_key;//线程私有数据的键值
void item_lock(uint32_t hv) {
//获取线程私有变量
uint8_t *lock_type = pthread_getspecific(item_lock_