使用读写锁
配置读写锁的属性之后,即可初始化读写锁。以下函数用于初始化或销毁读写锁、锁定或解除锁定读写锁或尝试锁定读写锁。下表列出了本节中讨论的用来处理读写锁的函数。
表 4–9 处理读写锁的例程 操作 | 相关函数说明 |
---|---|
初始化读写锁 | pthread_rwlock_init 语法 |
读取读写锁中的锁 | pthread_rwlock_rdlock 语法 |
读取非阻塞读写锁中的锁 | pthread_rwlock_tryrdlock 语法 |
写入读写锁中的锁 | pthread_rwlock_wrlock 语法 |
写入非阻塞读写锁中的锁 | pthread_rwlock_trywrlock 语法 |
解除锁定读写锁 | pthread_rwlock_unlock 语法 |
销毁读写锁 | pthread_rwlock_destroy 语法 |
初始化读写锁
使用 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 指定的值无效。
在Linux平台上已经有现成的线程读写锁pthread_rwlock_t以及相关API,现将这些API封装成与Win32平台上相同的接口,以便于编写跨平台程序。这些API包括pthread_rwlock_init,pthread_rwlock_rdlock,pthread_rwlock_tryrdlock,pthread_rwlock_wrlock,pthread_rwlock_trywrlock,pthread_rwlock_unlock,pthread_rwlock_destroy,可在Linux在线手册上查阅它们的说明。下边的代码在VS2005中编辑,在Fedora 13虚拟机中编译,测试通过。
RWLockImpl.h
- #ifndef _RWLockImpl_Header
- #define _RWLockImpl_Header
- #include <iostream>
- #include <pthread.h>
- #include <errno.h>
- #include <assert.h>
- using namespace std;
- /*
- 读写锁允许当前的多个读用户访问保护资源,但只允许一个写读者访问保护资源
- */
- //-----------------------------------------------------------------
- class CRWLockImpl
- {
- protected:
- CRWLockImpl();
- ~CRWLockImpl();
- void ReadLockImpl();
- bool TryReadLockImpl();
- void WriteLockImpl();
- bool TryWriteLockImpl();
- void UnlockImpl();
- private:
- pthread_rwlock_t m_rwl;
- };
- //-----------------------------------------------------------------
- class CMyRWLock: private CRWLockImpl
- {
- public:
- //创建读/写锁
- CMyRWLock(){};
- //销毁读/写锁
- ~CMyRWLock(){};
- //获取读锁
- //如果其它一个线程占有写锁,则当前线程必须等待写锁被释放,才能对保护资源进行访问
- void ReadLock();
- //尝试获取一个读锁
- //如果获取成功,则立即返回true,否则当另一个线程占有写锁,则返回false
- bool TryReadLock();
- //获取写锁
- //如果一个或更多线程占有读锁,则必须等待所有锁被释放
- //如果相同的一个线程已经占有一个读锁或写锁,则返回结果不确定
- void WriteLock();
- //尝试获取一个写锁
- //如果获取成功,则立即返回true,否则当一个或更多其它线程占有读锁,返回false
- //如果相同的一个线程已经占有一个读锁或写锁,则返回结果不确定
- bool TryWriteLock();
- //释放一个读锁或写锁
- void Unlock();
- private:
- CMyRWLock(const CMyRWLock&);
- CMyRWLock& operator = (const CMyRWLock&);
- };
- inline void CMyRWLock::ReadLock()
- {
- ReadLockImpl();
- }
- inline bool CMyRWLock::TryReadLock()
- {
- return TryReadLockImpl();
- }
- inline void CMyRWLock::WriteLock()
- {
- WriteLockImpl();
- }
- inline bool CMyRWLock::TryWriteLock()
- {
- return TryWriteLockImpl();
- }
- inline void CMyRWLock::Unlock()
- {
- UnlockImpl();
- }
- #endif
- CRWLockImpl::CRWLockImpl()
- {
- if (pthread_rwlock_init(&m_rwl, NULL))
- cout<<"cannot create reader/writer lock"<<endl;
- }
- CRWLockImpl::~CRWLockImpl()
- {
- pthread_rwlock_destroy(&m_rwl);
- }
- void CRWLockImpl::ReadLockImpl()
- {
- if (pthread_rwlock_rdlock(&m_rwl))
- cout<<"cannot lock reader/writer lock"<<endl;
- }
- bool CRWLockImpl::TryReadLockImpl()
- {
- int rc = pthread_rwlock_tryrdlock(&m_rwl);
- if (rc == 0)
- return true;
- else if (rc == EBUSY)
- return false;
- else
- cout<<"cannot lock reader/writer lock"<<endl;
- return false;
- }
- void CRWLockImpl::WriteLockImpl()
- {
- if (pthread_rwlock_wrlock(&m_rwl))
- cout<<"cannot lock reader/writer lock"<<endl;
- }
- bool CRWLockImpl::TryWriteLockImpl()
- {
- int rc = pthread_rwlock_trywrlock(&m_rwl);
- if (rc == 0)
- return true;
- else if (rc == EBUSY)
- return false;
- else
- cout<<"cannot lock reader/writer lock"<<endl;
- return false;
- }
- void CRWLockImpl::UnlockImpl()
- {
- if (pthread_rwlock_unlock(&m_rwl))
- cout<<"cannot unlock reader/writer lock"<<endl;
- }
测试代码
- // pthread_rwlock.cpp : 定义控制台应用程序的入口点。
- //
- #include "RWLockImpl.h"
- //创建一个读写锁对象
- CMyRWLock g_myRWLock;
- volatile int g_counter = 0;
- //线程函数
- void * StartThread(void *pParam)
- {
- int lastCount = 0;
- for (int i = 0; i < 10000; ++i)
- {
- g_myRWLock.ReadLock();
- lastCount = g_counter;
- //在读锁域,两个线程不断循环交替访问全局变量g_counter
- for (int k = 0; k < 100; ++k)
- {
- if (g_counter != lastCount)
- cout<<"the value of g_counter has been updated."<<endl;
- sleep(0);
- }
- g_myRWLock.Unlock();
- g_myRWLock.WriteLock();
- //在写锁域,只有一个线程可以修改全局变量g_counter的值
- for (int k = 0; k < 100; ++k)
- {
- --g_counter;
- sleep(0);
- }
- for (int k = 0; k < 100; ++k)
- {
- ++g_counter;
- sleep(0);
- }
- ++g_counter;
- if (g_counter <= lastCount)
- cout<<"the value of g_counter is error."<<endl;
- g_myRWLock.Unlock();
- }
- return (void *)0;
- }
- int main(int argc, char* argv[])
- {
- pthread_t thread1,thread2;
- pthread_attr_t attr1,attr2;
- //创建两个工作线程
- pthread_attr_init(&attr1);
- pthread_attr_setdetachstate(&attr1,PTHREAD_CREATE_JOINABLE);
- if (pthread_create(&thread1,&attr1, StartThread,0) == -1)
- {
- cout<<"Thread 1: create failed"<<endl;
- }
- pthread_attr_init(&attr2);
- pthread_attr_setdetachstate(&attr2,PTHREAD_CREATE_JOINABLE);
- if (pthread_create(&thread2,&attr2, StartThread,0) == -1)
- {
- cout<<"Thread 2: create failed"<<endl;
- }
- //等待线程结束
- void *result;
- pthread_join(thread1,&result);
- pthread_join(thread2,&result);
- //关闭线程,释放资源
- pthread_attr_destroy(&attr1);
- pthread_attr_destroy(&attr2);
- cout<<"the g_counter = "<<g_counter<<endl;
- int iWait;
- cin>>iWait;
- return 0;
- }