Linux多线程基础学习(五)线程同步-读写锁

读写锁


基础概念


    通过读写锁,可以对受保护的共享资源进行并发读取和独占写入。读写锁是可以在读取或写入模式下锁定的单一实体。要修改资源,线程必须首先获取互斥写锁。必须释放所有读锁之后,才允许使用互斥写锁。

     对数据库的访问可以使用读写锁进行同步。读写锁支持并发读取数据库记录,因为读操作不会更改记录的信息。要更新数据库时,写操作必须获取互斥写锁。

 

     在一些程序中存在读者写者问题,也就是说,对某些资源的访问会  存在两种可能的情况,一种是访问必须是排它行的,就是独占的意思,这称作写操作;另一种情况就是访问方式可以是共享的,就是说可以有多个线程同时去访问某个资源,这种就称作读操作。这个问题模型是从对文件的读写操作中引申出来的。

      读写锁比起mutex具有更高的适用性,具有更高的并行性,可以有多个线程同时占用读模式的读写锁,但是只能有一个线程占用写模式的读写锁,读写锁的三种状态:

      1.当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞

      2.当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是以写模式对它进行加锁的线程将会被阻塞

      3.当读写锁在读模式的锁状态时,如果有另外的线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁的请求,这样可以避免读模式锁长期占用,而等待的写模式锁请求则长期阻塞。

      读写锁最适用于对数据结构的读操作次数多于写操作的场合,因为,读模式锁定时可以共享,而写模式锁定时只能某个线程独占资源,因而,读写锁也可以叫做个共享-独占锁。

     处理读者-写者问题的两种常见策略是强读者同步(strong reader synchronization)和强写者同步(strong writersynchronization).    在强读者同步中,总是给读者更高的优先权,只要写者当前没有进行写操作,读者就可以获得访问权限;而在强写者同步中,则往往将优先权交付给写者,而读者只能等到所有正在等待的或者是正在执行的写者结束以后才能执行。关于读者-写者模型中,由于读者往往会要求查看最新的信息记录,所以航班订票系统往往会使用强写者同步策略,而图书馆查阅系统则采用强读者同步策略。

 

pthread_rwlockattr_init()//初始化读写锁属性
pthread_rwlockattr_destroy()//销毁读写锁属性
pthread_rwlockattr_setpshared()//设置读写锁属性
pthread_rwlockattr_getpshared()//获取读写锁属性
 

pthread_rwlock_init()//初始化读写锁
pthread_rwlock_rdlock()//读取读写锁中的锁
pthread_rwlock_tryrdlock()//读取非阻塞读写锁中的锁
pthread_rwlock_wrlock()//写入读写锁中的锁
pthread_rwlock_trywrlock()//写入非阻塞读写锁中的锁
pthread_rwlock_unlock()//解除锁定读写锁
pthread_rwlock_destroy()//销毁读写锁

1 初始化读写锁属性


intpthread_rwlockattr_init(pthread_rwlockattr_t *attr);

      如果调用pthread_rwlockattr_init来指定已初始化的读写锁属性对象,则结果是不确定的。读写锁属性对象初始化一个或多个读写锁之后,影响该对象的任何函数(包括销毁)不会影响先前已初始化的读写锁。

2 销毁读写锁属性


intpthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);

       在再次调用pthread_rwlockattr_init()重新初始化该对象之前,使用该对象所产生的影响是不确定的。实现可以导致pthread_rwlockattr_destroy()将attr所引用的对象设置为无效值。

 

3 设置读写锁属性

 

int pthread_rwlockattr_setpshared(pthread_rwlockattr_t*attr, int pshared);

      读写锁属性可以为以下值之一:

 

PTHREAD_PROCESS_SHARED

描述:允许可访问用于分配读写锁的内存的任何线程对读写锁进行处理。即使该锁是在由多个进程共享的内存中分配的,也允许对其进行处理。

 

PTHREAD_PROCESS_PRIVATE

描述:读写锁只能由某些线程处理,这些线程与初始化该锁的线程在同一进程中创建。如果不同进程的线程尝试对此类读写锁进行处理,则其行为是不确定的。由进程共享的属性的缺省值为PTHREAD_PROCESS_PRIVATE。

 

配置读写锁的属性之后,即可初始化读写锁。以下函数用于初始化或销毁读写锁、锁定或解除锁定读写锁或尝试锁定读写锁。


4 初始化读写锁


使用pthread_rwlock_init()可以通过attr所引用的属性初始化rwlock所引用的读写锁。

 

动态内存的初始化:

int pthread_rwlock_init(pthread_rwlock_t *rwlock,constpthread_rwlockattr_t *attr);

 

         如果attr为NULL,则使用缺省的读写锁属性,其作用与传递缺省读写锁属性对象的地址相同。初始化读写锁之后,该锁可以使用任意次数,而无需重新初始化。成功初始化之后,读写锁的状态会变为已初始化和未锁定。如果调用pthread_rwlock_init()来指定已初始化的读写锁,则结果是不确定的。如果读写锁在使用之前未初始化,则结果是不确定的。

 

静态初始化:

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

 

        如果缺省的读写锁属性适用,则PTHREAD_RWLOCK_INITIALIZER宏可初始化以静态方式分配的读写锁,其作用与通过调用pthread_rwlock_init()并将参数attr指定为NULL进行动态初始化等效,区别在于不会执行错误检查。

 

5 获取读写锁中的读锁

 

pthread_rwlock_rdlock()可用来向rwlock所引用的读写锁应用读锁。

 

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock );

       如果写入器未持有读锁,并且没有任何写入器基于该锁阻塞,则调用线程会获取读锁。如果写入器未持有读锁,但有多个写入器正在等待该锁时,调用线程是否能获取该锁是不确定的。如果某个写入器持有读锁,则调用线程无法获取该锁。如果调用线程未获取读锁,则它将阻塞。调用线程必须获取该锁之后,才能从pthread_rwlock_rdlock()返回。如果在进行调用时,调用线程持有rwlock中的写锁,则结果是不确定的。

       为避免写入器资源匮乏,允许在多个实现中使写入器的优先级高于读取器。

一个线程可以在rwlock中持有多个并发的读锁,该线程可以成功调用pthread_rwlock_rdlock()n次。该线程必须调用pthread_rwlock_unlock()n次才能执行匹配的解除锁定操作。如果针对未初始化的读写锁调用pthread_rwlock_rdlock(),则可能发生未知错误。

      线程信号处理程序可以处理传送给等待读写锁的线程的信号。从信号处理程序返回后,线程将继续等待读写锁以执行读取,就好像线程未中断一样。

 

6 读取非阻塞读写锁中的锁


        pthread_rwlock_tryrdlock()应用读锁的方式与pthread_rwlock_rdlock()类似,区别在于如果任何线程持有rwlock中的写锁或者写入器基于rwlock阻塞,则pthread_rwlock_tryrdlock()函数会失败。

intpthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

返回值:

    如果获取了用于在rwlock所引用的读写锁对象中执行读取的锁,则将返回零。如果没有获取该锁,则返回用于指明错误的错误号EBUSY。

    EBUSY:无法获取读写锁以执行读取,因为写入器持有该锁或者基于该锁已阻塞。

7 写入读写锁中的锁


       pthread_rwlock_wrlock()可用来向rwlock所引用的读写锁应用写锁。

 

intpthread_rwlock_wrlock(pthread_rwlock_t *rwlock );

 

      如果没有其他读取器线程或写入器线程持有读写锁rwlock,则调用线程将获取写锁。否则,调用线程将阻塞。调用线程必须获取该锁之后,才能从pthread_rwlock_wrlock()调用返回。如果在进行调用时,调用线程持有读写锁(读锁或写锁),则结果是不确定的。

      为避免写入器资源匮乏,允许在多个实现中使写入器的优先级高于读取器。

      如果针对未初始化的读写锁调用pthread_rwlock_wrlock(),则结果是不确定的。

      线程信号处理程序可以处理传送给等待读写锁以执行写入的线程的信号。从信号处理程序返回后,线程将继续等待读写锁以执行写入,就好像线程未中断一样。

8 写入非阻塞读写锁中的锁


       pthread_rwlock_trywrlock()应用写锁的方式与pthread_rwlock_wrlock()类似,区别在于如果任何线程当前持有用于读取和写入的rwlock,则pthread_rwlock_trywrlock()函数会失败。

intpthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

 

        如果针对未初始化的读写锁调用pthread_rwlock_trywrlock(),则结果是不确定的。

       线程信号处理程序可以处理传送给等待读写锁以执行写入的线程的信号。从信号处理程序返回后,线程将继续等待读写锁以执行写入,就好像线程未中断一样。

返回值

      如果获取了用于在rwlock引用的读写锁对象中执行写入的锁,则返回零。否则,将返回用于指明错误的错误号EBUSY。

       EBUSY:无法为写入获取读写锁,因为已为读取或写入锁定该读写锁。

 

9 解除锁定读写锁


      pthread_rwlock_unlock()可用来释放在rwlock引用的读写锁对象中持有的锁。

intpthread_rwlock_unlock (pthread_rwlock_t *rwlock);

 

        如果调用线程未持有读写锁rwlock,则结果是不确定的。

       如果通过调用pthread_rwlock_unlock()来释放读写锁对象中的读锁,并且其他读锁当前由该锁对象持有,则该对象会保持读取锁定状态。如果pthread_rwlock_unlock()释放了调用线程在该读写锁对象中的最后一个读锁,则调用线程不再是该对象的属主。如果pthread_rwlock_unlock()释放了该读写锁对象的最后一个读锁,则该读写锁对象将处于无属主、解除锁定状态。

      如果通过调用pthread_rwlock_unlock()释放了该读写锁对象的最后一个写锁,则该读写锁对象将处于无属主、解除锁定状态。

      如果pthread_rwlock_unlock()解除锁定该读写锁对象,并且多个线程正在等待获取该对象以执行写入,则通过调度策略可确定获取该对象以执行写入的线程。如果多个线程正在等待获取读写锁对象以执行读取,则通过调度策略可确定等待线程获取该对象以执行写入的顺序。如果多个线程基于rwlock中的读锁和写锁阻塞,则无法确定读取器和写入器谁先获得该锁。

     如果针对未初始化的读写锁调用pthread_rwlock_unlock(),则结果是不确定的。

 

10 销毁读写锁


     pthread_rwlock_destroy(3C)可用来销毁rwlock引用的读写锁对象并释放该锁使用的任何资源。

intpthread_rwlock_destroy(pthread_rwlock_t *rwlock);

 

    pthread_rwlock_trwlock = PTHREAD_RWLOCK_INITIALIZER;

      在再次调用pthread_rwlock_init()重新初始化该锁之前,使用该锁所产生的影响是不确定的。实现可能会导致pthread_rwlock_destroy()将rwlock所引用的对象设置为无效值。

      如果在任意线程持有rwlock时调用pthread_rwlock_destroy(),则结果是不确定的。尝试销毁未初始化的读写锁会产生不确定的行为。

     已销毁的读写锁对象可以使用pthread_rwlock_init()来重新初始化。销毁读写锁对象之后,如果以其他方式引用该对象,则结果是不确定的。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值