读写锁在对资源进行保护的同时,还能区分想要读取资源值的线程(读取者线程)和想要更新资源的线程(写入者线程)。对于读取者线程,读写锁会容许他们并发的执行。当有写入者线程在占有资源时,读写锁会让其它写入者线程和读取者线程等待。所以用读写锁来解决读者写者问题会使代码很是清晰和简洁。html
SRWLock程序员
从visual studio2005开始你可使用SRWLock,和CRITICAL_SECTION(关键段)差不过的功能,不一样的是由程序员控制读写线程,若是是读线程,能够同时读取,若是是写线程,则其余线程挂起,写完后立刻就能够读取windows
首先,须要一个SRWLOCK结构,而后调用InitializeSRWLock(PSWRLOCK srwLock)函数初始化这个结构.api
SRWLOCK srw; InitializeSRWLock(&srw); 一旦初始化完成,就能够对写入线程调用AcquireSRWLockExclusive()函数和ReleaseSRWLockExclusive()函数 AcquireSRWLockExclusive(&srw); //...写入数据,写入东西的时候该线程独占,其余任何线程不可进入 ReleaseSRWLockExclusive(&srw); 对于只读线程能够调用AcquireSRWLockShared()函数和ReleaseSRWLockShared()函数,以下 AcquireSRWLockShared(&srw); //..读取数据,若是这时没有写入数据则多个读取线程能够进行 ReleaseSRWLockShared)&srw);
Slim读/写锁并发
SRWLock的目的和关键段相同:对一个资源进行保护,不让其它线程访问它。可是,与关键段不一样的是,SRWLock容许咱们区分哪些想要读取资源的值的线程(读取者线程)和想要更新资源的值的线程(写入者线程)。让全部的读取者线程在同一时刻访问共享资源应该是可行的,这是由于仅仅读取资源的值并不存在破坏数据的风险。只有当写入者线程想要对资源进行更新的时候才须要进行同步。在这种状况下,写入者线程想要对资源进行更新的时候才须要进行同步。在这种状况下,写入者线程应该独占对资源的访问权:任何其它线程,不管是读取者线程仍是写入者线程,都不容许访问资源。这就是SRWLock提供的所有功能。函数
首先,咱们须要分配一个SRWLOCK结构并用InitializeSRWLock函数对它进行初始化:性能
VOID InitializeSRWLock(PSRWLOCK SRWLock);ui
一旦SRWLock初始化完成以后,写入者线程就能够调用AcquireSRWLockExclusive,将SRWLOCK对象的地址做为参数传入,以尝试得到对被保护资源的独占访问权。.net
VOID AcquireSRWLockExclusive(PSRWLOCK SRWLock);线程
完成对资源的更新以后,应该调用ReleaseSRWLockExclusice,并将SRWLOCK对象的地址做为参数传入,这样就解除了对资源的锁定。
VOID ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
对读取者线程来讲,一样有两个步骤,单调用的是下面两个新的函数:
VOID AcquireSRWLockShared(PSRWLOCK SRWLock);
VOID ReleaseSRWLockShared(PSRWLOCK SRWLock);
不存在用来删除或销毁SRWLOCK的函数,系统会自动执行清理工做。
与关键段相比,SRWLock缺少下面两个特性:
1)不存在TryEnter(Shared/Exclusive)SRWLock 之类的函数:若是锁已经被占用,那么调用AcquireSRWLock(Shared/Exclusive) 会阻塞调用线程。
2)不能递归地调用SRWLOCK。也就是说,一个线程不能为了屡次写入资源而屡次锁定资源,而后再屡次调用ReleaseSRWLock* 来释放对资源的锁定。
总结,若是但愿在应用程序中获得最佳性能,那么首先应该尝试不要共享数据,而后依次使用volatile读取,volatile写入,Interlocked API,SRWLock以及关键段。当且仅当全部这些都不能知足要求的时候,再使用内核对象。由于每次等待和释放内核对象都须要在用户模式和内核模式之间切换,这种切换的CPU开销很是大。
具体参考代码:
//读者与写者问题继 读写锁SRWLock #include <stdio.h> #include <process.h> #include <windows.h> //设置控制台输出颜色 BOOL SetConsoleColor(WORD wAttributes) { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) return FALSE; return SetConsoleTextAttribute(hConsole, wAttributes); } const int READER_NUM = 5; //读者个数 //关键段和事件 CRITICAL_SECTION g_cs; SRWLOCK g_srwLock; //读者线程输出函数(变参函数的实现) void ReaderPrintf(char *pszFormat, ...) { va_list pArgList; va_start(pArgList, pszFormat); EnterCriticalSection(&g_cs); vfprintf(stdout, pszFormat, pArgList); LeaveCriticalSection(&g_cs); va_end(pArgList); } //读者线程函数 unsigned int __stdcall ReaderThreadFun(PVOID pM) { ReaderPrintf(" 编号为%d的读者进入等待中...n", GetCurrentThreadId()); //读者申请读取文件 AcquireSRWLockShared(&g_srwLock); //读取文件 ReaderPrintf("编号为%d的读者开始读取文件...n", GetCurrentThreadId()); Sleep(rand() % 100); ReaderPrintf(" 编号为%d的读者结束读取文件n", GetCurrentThreadId()); //读者结束读取文件 ReleaseSRWLockShared(&g_srwLock); return 0; } //写者线程输出函数 void WriterPrintf(char *pszStr) { EnterCriticalSection(&g_cs); SetConsoleColor(FOREGROUND_GREEN); printf(" %sn", pszStr); SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); LeaveCriticalSection(&g_cs); } //写者线程函数 unsigned int __stdcall WriterThreadFun(PVOID pM) { WriterPrintf("写者线程进入等待中..."); //写者申请写文件 AcquireSRWLockExclusive(&g_srwLock); //写文件 WriterPrintf(" 写者开始写文件....."); Sleep(rand() % 100); WriterPrintf(" 写者结束写文件"); //标记写者结束写文件 ReleaseSRWLockExclusive(&g_srwLock); return 0; } int main() { printf(" 读者写者问题继 读写锁SRWLockn"); printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --nn"); //初始化读写锁和关键段 InitializeCriticalSection(&g_cs); InitializeSRWLock(&g_srwLock); HANDLE hThread[READER_NUM + 1]; int i; //先启动二个读者线程 for (i = 1; i <= 2; i++) hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL); //启动写者线程 hThread[0] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL); Sleep(50); //最后启动其它读者结程 for ( ; i <= READER_NUM; i++) hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL); WaitForMultipleObjects(READER_NUM + 1, hThread, TRUE, INFINITE); for (i = 0; i < READER_NUM + 1; i++) CloseHandle(hThread[i]); //销毁关键段 DeleteCriticalSection(&g_cs); return 0; }