读写锁
索引:
- 初始化一个读写锁pthread_rwlock_init
- 读锁定读写锁 pthread_rwlock_rdlock
- 非阻塞读锁定 pthread_rwlock_tryrdlock
- 写锁定读写锁 pthread_rwlock_wrlock
- 非阻塞写锁定 pthread_rwlock_trywrlock
- 解锁读写锁 pthread_rwlock_unlock
- 释放读写锁 pthread_rwlock_destroy
读写锁是用来解决读者写者问题的,读操作可以共享,写操作是排他的,读可以有多个在读,写只有唯一个在写,同时写的时候不允许读。
具有强读者同步和强写者同步两种形式:
强读者同步:当写者没有进行写操作,读者就可以访问;
强写者同步:当所有写者都写完之后,才能进行读操作,读者需要最新的信息,一些事实性较高的系统可能会用到该所,比如定票之类的。
1.初始化一个读写锁pthread_rwlock_init
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *rwlock, \ const pthread_rwlockattr_t *attr);
返回值:函数成功返回0;任何其他返回值都表示错误
初始化rwlock指定的读写锁,它的属性被参数attr指定。如果attr的值为NULL,初始化读写锁时使用缺省的读写锁属性。其效果和attr指向一个缺省的读写锁属性对象是一样的。
成功初始化后,读写锁的状态为非锁定的。
对一个已经初始化过的读写锁调用pthread_rwlock_init函数会产生不可预测的后果。使用一个未经初始化的读写锁也会产生不可预测的后果。
如果需要缺省属性的读写锁,可以用宏PTHREAD_RWLOCK_INITIALIZER初始化静态的读写锁变量。但是,静态初始化读写锁不进行错误检查。如:
pthread_rwlock_t rwlock = PTHREASD_RWLOCK_INITIALIZER;
获取读写锁的读锁操作:分为阻塞式获取和非阻塞式获取,如果读写锁由一个写者持有,则读线程会阻塞直至写入者释放读写锁。
2.读锁定读写锁pthread_rwlock_rdlock
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
返回值:函数成功返回0;任何其他返回值都表示错误
函数在rwlock读写锁上进行读锁定。
如果一个线程写锁定了读写锁,调用pthread_rwlock_rdlock函数的线程将无法读锁定读写锁,并将被阻塞,直到线程可以读锁定这个读写锁为止。
如果一个线程写锁定了读写锁后又调用pthread_rwlock_rdlock函数来读锁定同一个读写锁,结果将无法预测。
标准中没有指定当没有线程写锁定这个读写锁,但有线程试图写锁定而被阻塞在这个读写锁上时,当前线程是否能读锁定读写锁。大多数线程库往往优先考虑激活试图写锁定而被阻塞的线程,这样做是为了防止试图写锁定而被阻塞的线程长时间得不到调度。
一个线程可能对一个读写锁进行了多次读锁定(即成功调用了pthread_rwlock_rdlock()函数多次)。如果是这样的,线程必须调用pthread_rwlock_unlock()函数对读写锁进行同样次数的解锁。
当一个读写锁被读锁定了,而一个线程阻塞在这个读写锁上时,如果这时来了一个信号,那么当线程从信号处理程序中返回时,将继续阻塞在这个读写锁上。就好象线程没有被中断过。
3.非阻塞读锁定pthread_rwlock_tryrdlock
#include <pthread.h>
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
返回值:函数成功返回0;任何其他返回值都表示错误
pthread_rwlock_tryrdlock()函数和pthread_rwlock_rdlock函数的功能相近,不同的是,当已有线程写锁定读写锁,或是有试图写锁定的线程被阻塞时,pthread_rwlock_tryrdlock函数失败返回。
获取读写锁的写锁操作:分为阻塞和非阻塞,如果对应的读写锁被其它写者持有,或者读写锁被读者持有,该线程都会阻塞等待。
4.写锁定读写锁pthread_rwlock_wrlock
#include <pthread.h>
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
返回值:函数成功返回0;任何其他返回值都表示错误
写锁定读写锁rwlock。如果没有线程读或写锁定读写锁rwlock,当前线程将写锁定读写锁rwlock。否则线程将被阻塞,直到没有线程锁定这个读写锁为止。
如果调用这个函数之前,本线程已经读或写锁定了这个读写锁,那么pthread_rwlock_wrlock函数运行的结果是不可预测的。
读写锁被解开以后,激活阻塞在读写锁上的线程时,往往优先考虑试图写锁定而被阻塞的线程,这样做是为了防止试图写锁定而被阻塞的线程长时间得不到调度。
当一个读写锁被写锁定了,而一个线程阻塞在这个读写锁上时,如果这时来了一个信号,那么当线程从信号处理程序中返回时,将继续阻塞在这个读写锁上。就好像线程没有被中断过。
5.非阻塞写锁定pthread_rwlock_trywrlock
#include <pthread.h>
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
返回值:函数成功返回0;任何其他返回值都表示错误
pthread_rwlock_trywrlock函数的功能和pthread_rwlock_wrlock函数相近。不同的是如果有其他线程锁定了读写锁,pthread_rwlock_trywrlock函数会失败返回。
6.解锁读写锁pthread_rwlock_unlock
#include <pthread.h>
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
返回值:函数成功返回0;任何其他返回值都表示错误
解锁一个读写锁。
调用pthread_rwlock_unlock函数之前必须调用pthread_rwlock_rdlock函数或pthread_rwlock_wrlock函数锁定读写锁。否则结果是不可预测的。
在pthread_rwlock_unlock函数被用来解锁对读写锁的读锁定后,如果本线程对这个读写锁还有其他读锁定,那么这个读写锁对本线程将继续保持读锁定状态。如果pthread_rwlock_unlock函数解开了当前线程在这个读写锁上的最后一个读锁定,那么当前线程将不再拥有对这个读写锁的读锁定。如果pthread_rwlock_unlock函数解开了这个读写锁上的最后一个锁定,那么这个读写锁将处在非锁定状态。
如果pthread_rwlock_unlock函数被用来解锁对读写锁的写锁定,那么函数返回后,这个读写锁将处在非锁定状态。
如果用pthread_rwlock_unlock函数解锁一个读写锁时,有多个线程在等待对这个读写锁进行写锁定,系统将用调度策略决定激活哪个线程对读写锁进行写锁定。
如果用pthread_rwlock_unlock函数解锁一个读写锁时,有多个线程在等待对这个读写锁进行读锁定,系统将用调度策略决定按什么顺序激活各个线程对读写锁进行读锁定。
如果用pthread_rwlock_unlock函数解锁一个读写锁时,有多个线程在等待对这个读写锁进行写锁定和读锁定,一般先激活需要写锁定的线程对读写锁进行写锁定(标准没有指定在这种情况下应该先激活写线程还是先激活读线程)。
7.释放读写锁pthread_rwlock_destroy
#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
返回值:函数成功返回0;任何其他返回值都表示错误
释放读写锁rwlock,并释放这个读写锁占用的资源。一般情况下,pthread_rwlock_destroy函数将rwlock指向的互斥锁对象设置为非法值。
释放了一个读写锁之后,在pthread_rwlock_init重新初始化它之前,再使用这个读写锁会有不可预料的结果。
如果有线程锁定了某个读写锁,那么用pthread_rwlock_destroy()函数释放这个读写锁会引起不可预知的结果。
试图释放一个未初始化的读写锁会引起不可预知的结果。
总结(转):
互斥锁与读写锁的区别:
当访问临界区资源时(访问的含义包括所有的操作:读和写),需要上互斥锁;
当对数据(互斥锁中的临界区资源)进行读取时,需要上读取锁,当对数据进行写入时,需要上写入锁。
读写锁的优点:
对于读数据比修改数据频繁的应用,用读写锁代替互斥锁可以提高效率。因为使用互斥锁时,即使是读出数据(相当于操作临界区资源)都要上互斥锁,而采用读写锁,则可以在任一时刻允许多个读出者存在,提高了更高的并发度,同时在某个写入者修改数据期间保护该数据,以免任何其它读出者或写入者的干扰。
读写锁描述:
获取一个读写锁用于读称为共享锁,获取一个读写锁用于写称为独占锁,因此这种对于某个给定资源的共享访问也称为共享-独占上锁。
有关这种类型问题(多个读出者和一个写入者)的其它说法有读出者与写入者问题以及多读出者-单写入者锁。
==========================================================================
转自:http://blog.youkuaiyun.com/onlyou930/article/details/6755593
使用读写锁
配置读写锁的属性之后,即可初始化读写锁。以下函数用于初始化或销毁读写锁、锁定或解除锁定读写锁或尝试锁定读写锁。下表列出了本节中讨论的用来处理读写锁的函数。
表 4–9 处理读写锁的例程
操作 | 相关函数说明 |
---|---|
初始化读写锁 | |
读取读写锁中的锁 | |
读取非阻塞读写锁中的锁 | |
写入读写锁中的锁 | |
写入非阻塞读写锁中的锁 | |
解除锁定读写锁 | |
销毁读写锁 |
初始化读写锁
使用 pthread_rwlock_init(3C) 可以通过 attr 所引用的属性初始化 rwlock 所引用的读写锁。
pthread_rwlock_init 语法
#include <pthread.h> int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr); pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
如果 attr 为 NULL,则使用缺省的读写锁属性,其作用与传递缺省读写锁属性对象的地址相同。初始化读写锁之后,该锁可以使用任意次数,而无需重新初始化。成功初始化之后,读写锁的状态会变为已初始化和未锁定。如果调用 pthread_rwlock_init() 来指定已初始化的读写锁,则结果是不确定的。如果读写锁在使用之前未初始化,则结果是不确定的。对于 Solaris 线程,请参见rwlock_init 语法。
如果缺省的读写锁属性适用,则 PTHREAD_RWLOCK_INITIALIZER 宏可初始化以静态方式分配的读写锁,其作用与通过调用 pthread_rwlock_init() 并将参数attr 指定为 NULL 进行动态初始化等效,区别在于不会执行错误检查。
pthread_rwlock_init 返回值
如果成功,pthread_rwlock_init() 会返回零。否则,将返回用于指明错误的错误号。
如果 pthread_rwlock_init() 失败,将不会初始化 rwlock,并且 rwlock 的内容是不确定的。
EINVAL
描述:
attr 或 rwlock 指定的值无效。
获取读写锁中的读锁
pthread_rwlock_rdlock(3C) 可用来向 rwlock 所引用的读写锁应用读锁。
pthread_rwlock_rdlock 语法
#include <pthread.h> int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock );
如果写入器未持有读锁,并且没有任何写入器基于该锁阻塞,则调用线程会获取读锁。如果写入器未持有读锁,但有多个写入器正在等待该锁时,调用线程是否能获取该锁是不确定的。如果某个写入器持有读锁,则调用线程无法获取该锁。如果调用线程未获取读锁,则它将阻塞。调用线程必须获取该锁之后,才能从 pthread_rwlock_rdlock() 返回。如果在进行调用时,调用线程持有 rwlock 中的写锁,则结果是不确定的。
为避免写入器资源匮乏,允许在多个实现中使写入器的优先级高于读取器。例如,Solaris 线程实现中写入器的优先级高于读取器。 请参见rw_rdlock 语法。
一个线程可以在 rwlock 中持有多个并发的读锁,该线程可以成功调用 pthread_rwlock_rdlock() n 次。该线程必须调用 pthread_rwlock_unlock() n 次才能执行匹配的解除锁定操作。
如果针对未初始化的读写锁调用 pthread_rwlock_rdlock(),则结果是不确定的。
线程信号处理程序可以处理传送给等待读写锁的线程的信号。从信号处理程序返回后,线程将继续等待读写锁以执行读取,就好像线程未中断一样。
pthread_rwlock_rdlock 返回值
如果成功,pthread_rwlock_rdlock() 会返回零。否则,将返回用于指明错误的错误号。
EINVAL
描述:
attr 或 rwlock 指定的值无效。
读取非阻塞读写锁中的锁
pthread_rwlock_tryrdlock(3C) 应用读锁的方式与 pthread_rwlock_rdlock() 类似,区别在于如果任何线程持有 rwlock 中的写锁或者写入器基于 rwlock 阻塞,则 pthread_rwlock_tryrdlock() 函数会失败。对于 Solaris 线程,请参见rw_tryrdlock 语法。
pthread_rwlock_tryrdlock 语法
#include <pthread.h> int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
pthread_rwlock_tryrdlock 返回值
如果获取了用于在 rwlock 所引用的读写锁对象中执行读取的锁,则 pthread_rwlock_tryrdlock() 将返回零。如果没有获取该锁,则返回用于指明错误的错误号。
EBUSY
描述:
无法获取读写锁以执行读取,因为写入器持有该锁或者基于该锁已阻塞。
写入读写锁中的锁
pthread_rwlock_wrlock(3C) 可用来向 rwlock 所引用的读写锁应用写锁。
pthread_rwlock_wrlock 语法
#include <pthread.h> int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock );
如果没有其他读取器线程或写入器线程持有读写锁 rwlock,则调用线程将获取写锁。否则,调用线程将阻塞。调用线程必须获取该锁之后,才能从pthread_rwlock_wrlock() 调用返回。如果在进行调用时,调用线程持有读写锁(读锁或写锁),则结果是不确定的。
为避免写入器资源匮乏,允许在多个实现中使写入器的优先级高于读取器。(例如,Solaris 线程实现允许写入器的优先级高于读取器。请参见rw_wrlock 语法。)
如果针对未初始化的读写锁调用 pthread_rwlock_wrlock(),则结果是不确定的。
线程信号处理程序可以处理传送给等待读写锁以执行写入的线程的信号。从信号处理程序返回后,线程将继续等待读写锁以执行写入,就好像线程未中断一样。
pthread_rwlock_wrlock 返回值
如果获取了用于在 rwlock 所引用的读写锁对象中执行写入的锁,则 pthread_rwlock_rwlock() 将返回零。如果没有获取该锁,则返回用于指明错误的错误号。
写入非阻塞读写锁中的锁
pthread_rwlock_trywrlock(3C) 应用写锁的方式与 pthread_rwlock_wrlock() 类似,区别在于如果任何线程当前持有用于读取和写入的 rwlock,则pthread_rwlock_trywrlock() 函数会失败。对于 Solaris 线程,请参见 rw_trywrlock 语法。
pthread_rwlock_trywrlock 语法
#include <pthread.h> int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
如果针对未初始化的读写锁调用 pthread_rwlock_trywrlock(),则结果是不确定的。
线程信号处理程序可以处理传送给等待读写锁以执行写入的线程的信号。从信号处理程序返回后,线程将继续等待读写锁以执行写入,就好像线程未中断一样。
pthread_rwlock_trywrlock 返回值
如果获取了用于在 rwlock 引用的读写锁对象中执行写入的锁,则 pthread_rwlock_trywrlock() 将返回零。否则,将返回用于指明错误的错误号。
EBUSY
描述:
无法为写入获取读写锁,因为已为读取或写入锁定该读写锁。
解除锁定读写锁
pthread_rwlock_unlock(3C) 可用来释放在 rwlock 引用的读写锁对象中持有的锁。
pthread_rwlock_unlock 语法
#include <pthread.h> int pthread_rwlock_unlock (pthread_rwlock_t *rwlock);
如果调用线程未持有读写锁 rwlock,则结果是不确定的。对于 Solaris 线程,请参见rw_unlock 语法。
如果通过调用 pthread_rwlock_unlock() 来释放读写锁对象中的读锁,并且其他读锁当前由该锁对象持有,则该对象会保持读取锁定状态。如果pthread_rwlock_unlock() 释放了调用线程在该读写锁对象中的最后一个读锁,则调用线程不再是该对象的属主。如果 pthread_rwlock_unlock() 释放了该读写锁对象的最后一个读锁,则该读写锁对象将处于无属主、解除锁定状态。
如果通过调用 pthread_rwlock_unlock() 释放了该读写锁对象的最后一个写锁,则该读写锁对象将处于无属主、解除锁定状态。
如果 pthread_rwlock_unlock() 解除锁定该读写锁对象,并且多个线程正在等待获取该对象以执行写入,则通过调度策略可确定获取该对象以执行写入的线程。如果多个线程正在等待获取读写锁对象以执行读取,则通过调度策略可确定等待线程获取该对象以执行写入的顺序。如果多个线程基于 rwlock 中的读锁和写锁阻塞,则无法确定读取器和写入器谁先获得该锁。
如果针对未初始化的读写锁调用 pthread_rwlock_unlock(),则结果是不确定的。
pthread_rwlock_unlock 返回值
如果成功,pthread_rwlock_unlock() 会返回零。否则,将返回用于指明错误的错误号。
销毁读写锁
pthread_rwlock_destroy(3C) 可用来销毁 rwlock 引用的读写锁对象并释放该锁使用的任何资源。
pthread_rwlock_destroy 语法
#include <pthread.h> int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
在再次调用 pthread_rwlock_init() 重新初始化该锁之前,使用该锁所产生的影响是不确定的。实现可能会导致 pthread_rwlock_destroy() 将 rwlock 所引用的对象设置为无效值。如果在任意线程持有 rwlock 时调用 pthread_rwlock_destroy(),则结果是不确定的。尝试销毁未初始化的读写锁会产生不确定的行为。已销毁的读写锁对象可以使用 pthread_rwlock_init() 来重新初始化。销毁读写锁对象之后,如果以其他方式引用该对象,则结果是不确定的。对于 Solaris 线程,请参见rwlock_destroy 语法。
pthread_rwlock_destroy 返回值
如果成功,pthread_rwlock_destroy() 会返回零。否则,将返回用于指明错误的错误号。
EINVAL
描述:
attr 或 rwlock 指定的值无效。
转自:http://hi.baidu.com/andyzcj/blog/item/081b7018cf41aa4b43a9ad87.html