Home glibc 读写锁pthread_rw_lock
Post
Cancel

glibc 读写锁pthread_rw_lock

读写锁

介绍

  • 读写锁允许更高的并行性,也叫共享互斥锁。互斥量要么是加锁状态,要么就是解锁状态,而且一次只有一个线程可以对其加锁。读写锁可以有3种状态:读模式下加锁状态、写模式加锁状态、不加锁状态。一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁,即允许多个线程读但只允许一个线程写。
  • 当读操作较多,写操作较少时,可用读写锁提高线程读并发性

具体实现:

以pthread_rwlock_rdlock为例,其他同理

实际有个互斥锁,保护读写锁的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//arm64
typedef union
{
  struct
  {
    int __lock;
    unsigned int __nr_readers;
    unsigned int __readers_wakeup;
    unsigned int __writer_wakeup;
    unsigned int __nr_readers_queued;
    unsigned int __nr_writers_queued;
    int __writer;
    int __shared;
    unsigned long int __pad1;
    unsigned long int __pad2;
    unsigned int __flags;
  } __data;
  char __size[__SIZEOF_PTHREAD_RWLOCK_T];
  long int __align;
} pthread_rwlock_t;

获取读锁:

x86:

1
2
//nptl/sysdeps/unix/sysv/linux/x86_64/pthread_rwlock_rdlock.S
//逻辑与arm64的一致

arm64:

基于glibc-2.17的代码分析:

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
//nptl/pthread_rwlock_rdlock.c
/*
 * 1 锁住rwlock.data.lock互斥锁
 * 2 判断当前是否有写者,实现不同路径
 *    1> 如果没有写者,并且是读者优先,即使有写者在等锁,也优先把锁给读者;
 	      ---->结束
 *    2> 如果有写者,释放rwlock.data.lock互斥锁, 阻塞等待写者释放锁后的唤醒。
 *        ----->获取rwlock.data.lock互斥锁, 重新获取读锁
 */
/* Acquire read lock for RWLOCK.  */
int
__pthread_rwlock_rdlock (rwlock)
     pthread_rwlock_t *rwlock;
{
    ...
    /* Make sure we are alone.  */
    lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);
    while (1)  {
      /* Get the rwlock if there is no writer...  */
       if (rwlock->__data.__writer == 0
      /* ...and if either no writer is waiting or we prefer readers.  */
      && (!rwlock->__data.__nr_writers_queued
          || PTHREAD_RWLOCK_PREFER_READER_P (rwlock))) {
           ++rwlock->__data.__nr_readers; //增加读者
           break; //结束
       }
       ++rwlock->__data.__nr_readers_queued; //读写入等待队列
       /* Free the lock.  */
      lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared); 

      /* Wait for the writer to finish.  */
      lll_futex_wait (&rwlock->__data.__readers_wakeup, waitval,
		      rwlock->__data.__shared);

      /* Get the lock.  */
      lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);

      --rwlock->__data.__nr_readers_queued; //读者等待队列自减,再重新去获取锁
    }
    lll_unlock(rwlock->__data.__lock);
}
1
2
3
4
5
6
//nptl/pthread_rwlock_wrlock.c
/* Acquire write lock for RWLOCK.  */
int
__pthread_rwlock_wrlock (rwlock)
     pthread_rwlock_t *rwlock;
{
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
//nptl/pthread_rwlock_unlock.c
/* Unlock RWLOCK.  */
int
__pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
{
    
  lll_lock (rwlock->__data.__lock, rwlock->__data.__shared); //获取rwlock数据保护的互斥锁
  if (rwlock->__data.__writer) //判断是读者 or 写者
    rwlock->__data.__writer = 0; //写着释放, 将写者的线程号
  else
    --rwlock->__data.__nr_readers;//读者自减
  if (rwlock->__data.__nr_readers == 0) //判断是否还有读者? 两种情况:1、 写者持锁,当前释放,存在读者等待 2、读者持锁,所有读者都释放锁,存在写者等待
    { 								//没有读者
      if (rwlock->__data.__nr_writers_queued) //判断是否有写者等待?
	{
	  ++rwlock->__data.__writer_wakeup; //存在读者等待,唤醒写者
	  lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared);
	  lll_futex_wake (&rwlock->__data.__writer_wakeup, 1,
			  rwlock->__data.__shared); //唤醒一个写者,写者抢锁
	  return 0;
	}
      else if (rwlock->__data.__nr_readers_queued) //此时写者unlock,存在读者等待。
	{
	  ++rwlock->__data.__readers_wakeup; //唤醒读者
	  lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared);
	  lll_futex_wake (&rwlock->__data.__readers_wakeup, INT_MAX,
			  rwlock->__data.__shared); //唤醒所有读者,并发读
	  return 0;
	}
    }
  lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared); //释放rwlock数据保护的互斥锁
  return 0;
}

性能测试

测试方案:

总结

对锁性能来看:

1、

This post is licensed under CC BY 4.0 by the author.