读写锁允许多个线程同时进行读取访问,但限制每次只能对一个线程进行写入访问。本节讨论了以下主题:
初始化读取器/写入器锁
获取读锁
尝试获取读锁
获取写锁
尝试获取写锁
解除锁定读取器/写入器锁
销毁读取器/写入器锁的状态
一个线程持有读锁时,其他线程也可以获取读锁,但要获取写锁则必须等待。 如果一个线程持有写锁或者正在等待获取写锁,则其他线程必须等待才能获取读锁或写锁。
相比互斥锁,读写锁速度较慢。但是,如果许多并发线程读取使用锁保护的数据,但不经常进行写入,则使用读写锁可以提高性能。
使用读写锁可以同步此进程和其他进程中的线程。读写锁是在可以写入并在协作进程之间共享的内存中分配的。有关针对此行为映射读写锁的信息,请参见 mmap(2) 手册页。
缺省情况下,多个线程正在等待读写锁时,获取锁的顺序是不确定的。但是,为了避免写入器资源匮乏,对于优先级相同的写入器和读取器,Solaris 线程软件包中写入器往往优先于读取器。
读写锁在使用之前必须先初始化。
使用 rwlock_init(3C) 可以初始化 rwlp 所指向的读写锁并将锁的状态设置为未锁定。
#include <synch.h> (或 #include <thread.h>) int rwlock_init(rwlock_t *rwlp, int type, void * arg);
type 可以是以下值之一:
同一个读写锁不能同时由多个线程初始化。读写锁还可以通过在清零的内存中进行分配来初始化,在这种情况下假设 type 为 USYNC_THREAD。对于其他线程可能正在使用的读写锁,不得重新初始化。
对于 POSIX 线程,请参见pthread_rwlock_init 语法。
#include <thread.h> rwlock_t rwlp; int ret; /* to be used within this process only */ ret = rwlock_init(&rwlp, USYNC_THREAD, 0);
#include <thread.h> rwlock_t rwlp; int ret; /* to be used among all processes */ ret = rwlock_init(&rwlp, USYNC_PROCESS, 0);
rwlock_init() 在成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述:参数无效。
EFAULT
描述:rwlp 或 arg 指向的地址非法。
使用 rw_rdlock(3C) 可以获取 rwlp 所指向的读写锁中的读锁。
#include <synch.h> (或 #include <thread.h>) int rw_rdlock(rwlock_t *rwlp);
如果读写锁中的写锁已经锁定,则调用线程将阻塞,直到释放写锁为止。否则,将获取读锁。对于 POSIX 线程,请参见pthread_rwlock_rdlock 语法。
rw_rdlock() 在成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述:参数无效。
EFAULT
描述:rwlp 指向的地址非法。
使用 rw_tryrdlock(3C) 可以尝试获取 rwlp 所指向的读写锁中的读锁。
#include <synch.h> (或 #include <thread.h>) int rw_tryrdlock(rwlock_t *rwlp);
如果读写锁中的写锁已经锁定,则 rw_tryrdlock() 将返回错误。否则,将获取读锁。对于 POSIX 线程,请参见pthread_rwlock_tryrdlock 语法。
rw_tryrdlock() 在成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述:参数无效。
EFAULT
描述:rwlp 指向的地址非法。
EBUSY
描述:rwlp 所指向的读写锁已经锁定。
使用 rw_wrlock(3C) 可以获取 rwlp 所指向的读写锁中的写锁。
#include <synch.h> (或 #include <thread.h>) int rw_wrlock(rwlock_t *rwlp);
如果读写锁中的读锁或写锁已经锁定,则调用线程将阻塞,直到释放所有的读锁和写锁为止。读写锁中的写锁一次只能由一个线程持有。对于 POSIX 线程,请参见pthread_rwlock_wrlock 语法。
rw_wrlock() 在成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述:参数无效。
EFAULT
描述:rwlp 指向的地址非法。
使用 rw_trywrlock(3C) 可以尝试获取 rwlp 所指向的读写锁中的写锁。
#include <synch.h> (或 #include <thread.h>) int rw_trywrlock(rwlock_t *rwlp);
如果读写锁上的读锁或写锁已经锁定,则 rw_trywrlock() 将返回错误。对于 POSIX 线程,请参见pthread_rwlock_trywrlock 语法。
rw_trywrlock() 在成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述:参数无效。
EFAULT
描述:rwlp 指向的地址非法。
EBUSY
描述:rwlp 所指向的读写锁已经锁定。
使用 rw_unlock(3C) 可以解除锁定 rwlp 所指向的读写锁。
#include <synch.h> (或 #include <thread.h>) int rw_unlock(rwlock_t *rwlp);
读写锁必须处于锁定状态,并且调用线程必须持有读锁或写锁。如果还有其他线程正在等待读写锁成为可用,则其中一个线程将被解除阻塞。对于 POSIX 线程,请参见pthread_rwlock_unlock 语法。
rw_unlock() 在成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述:参数无效。
EFAULT
描述:rwlp 指向的地址非法。
使用 rwlock_destroy(3C) 可以销毁与 rlwp 所指向的读写锁相关联的任何状态。
#include <synch.h> (或 #include <thread.h>) int rwlock_destroy(rwlock_t *rwlp);
用来存储读写锁的空间不会释放。对于 POSIX 线程,请参见pthread_rwlock_destroy 语法。
示例 8–1 使用银行帐户来说明读写锁。尽管该程序可能会允许多个线程对帐户余额进行并行只读访问,但是仅允许使用一个写入器。请注意,get_balance() 函数需要使用该锁才能确保以原子方式添加支票帐户余额和储蓄帐户余额。
rwlock_t account_lock; float checking_balance = 100.0; float saving_balance = 100.0; ... rwlock_init(&account_lock, 0, NULL); ... float get_balance() { float bal; rw_rdlock(&account_lock); bal = checking_balance + saving_balance; rw_unlock(&account_lock); return(bal); } void transfer_checking_to_savings(float amount) { rw_wrlock(&account_lock); checking_balance = checking_balance - amount; saving_balance = saving_balance + amount; rw_unlock(&account_lock); }
rwlock_destroy() 在成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述:参数无效。
EFAULT
描述:rwlp 指向的地址非法。