Yebangyu's Blog

Fond of Concurrency Programming , Distributed System and Machine Learning

实现可重入锁

基本概念

可重入锁(Reentrant Lock),是指允许同一个线程多次对该锁进行acquire动作。对于不可重入的锁,当一个线程多次调用acquire后将造成死锁。可重入锁具有广泛的应用,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Routine
{
  void f()
  {
    lock_.lock();
    h();
    g();
    lock_.unlock();
  }
  void g()
  {
    lock_.lock();
    cout<<"abc"<<endl;
    lock_.unlock();
  }
  void h()
  {
    lock_.lock();
    cout<<"def"<<endl;
    lock_.unlock();
  }
  Lock lock_;
};

在函数f里调用了g和h,而在每个函数里都试图对lock进行acquire操作。可能你会说保证只有一个函数加锁就行了,但是有时候很难做成这个样子,毕竟一般我们都要求任何一个public函数被调用时候都得保证是线程安全因此需要加锁的。

那么,reentrant lock和recursive lock有什么区别呢?根据wikipedia,这两个是一样的。

基本实现

自己实现一个可重入锁并不困难:我们增加两个field,一个用来记录锁当前被哪个线程拥有;一个用来记录尝试acquire的次数。如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdint.h>
#include <pthread.h>
Class MyReentrantLock
{
public:
  MyReentrantLock()
  {
    lock_holder_ = NULL;
    hold_counter_ = 0;
    pthread_mutex_init(&lock_, NULL);
  }
  int lock()
  {
    int ret = 0;
    pthread_t curr_id = pthread_self();
    if (lock_holder_ == curr_id) {
      ++hold_counter_;
    } else {
      pthread_mutex_lock(&lock_);
      lock_holder_ = curr_id;
      hold_counter_ = 1;
    }
    return ret;
  }
  int unlock()
  {
    int ret = 0;
    pthread_t curr_id = pthread_self();
    if (lock_holder_ != curr_id) {
      ret = -1;
    } else {
      if (--hold_counter_ == 0) {
        lock_holder_ = NULL;
        pthread_mutex_unlock(&lock_);
      }
    }
    return ret;
  }
private:
  pthread_mutex_t lock_;
  pthread_t lock_holder_;
  int64_t hold_counter_;
};

顺便说一句,默认pthread_mutex_t是不可重入的。为了让它可重入,可以这样:

1
2
3
4
5
pthread_mutex_t mutex;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);//设置可递归也就是可重入
pthread_mutex_init(&mutex, &attr);

参考文献

1,《The Art Of Multiprocessor Programming》 8.4节,187页

这本书的作者都是并发编程大仙级人物。虽然是用Java语言写的,但是还是很值得一读。

2,Implementing a Recursive Mutex

很好的博客,每篇都非常高水准。我们最近正在翻译其中和并发编程相关的文章。可以点击这里查看:深入探索并发编程系列