博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
动手实现读写锁
阅读量:4648 次
发布时间:2019-06-09

本文共 3279 字,大约阅读时间需要 10 分钟。

排他锁的弊端
     在多个线程之间共享数据,普遍做法是加锁读写,也就是同一个时刻只有一个线程能够读或者写,以保证数据一致性,即线程安全。例如下面的伪代码是常见的做法
1 void Read() 2 { 3     Lock(mutex); 4  5     // 读取数据 6  7     UnLock(mutex); 8 } 9 10 void Write()11 {12     Lock(mutex);13 14     // 写入数据15 16     UnLock(mutex);17 }

 

读写锁的设计

     这样的锁是具有排他性的,会在一定程度上影响程序的效率。假设有多个线程竞争获得读写权利,显然同一个时刻只有一个线程能够获得,要么进行读操作,要么进行写操作,而且,在读操作和读操作之间,或在写操作和写操作之间,同样具有排他性,存在下面的关系
1、读-写,排他
2、写-写,排他
3、读-读,排他
     既然排他锁影响程序效率,那么该如何优化呢?排他锁的目的,为了避免出现这样的竞争条件,在一个线程在未完成读操作之前,另一个线程写操作改变了数据,或者多个线程同时进行写操作。显然,在读操作和写操作之间,或在写操作和写操作之间,要求具有排他性,而排他锁带入到负面影响是,在读操作和读操作之间也具有了排他性。到这里,可以设想一下读写锁能够解决什么问题,没错,就是为了屏蔽这个负面影响,能够满足下面的关系
1、读-写,排他
2、写-写,排他
3、读-读,共享
     简单地说,就是多个线程能够同时进行读操作。思路如下(伪代码)
1 void Read() 2 { 3     Wait(condition); 4  5     Lock(mutex); 6     if(0 == Count++) 7         Lock(semaphore); 8     UnLock(mutex); 9 10     // 读取数据11 12     Lock(mutex);13     if(0 == --Count)14         UnLock(semaphore);15     UnLock(mutex);16 }17 18 void Write()19 {20     Destroy(condition);21     Lock(semaphore);22 23     // 写入数据24 25     UnLock(semaphore);26     Create(condition);27 }

  在代码中,信号量semaphore的初值为1,为了读-写能够排他和写-写排他这两个关系。Count记录有多少个读操作,而mutex是为了保证Count线程安全。忽略condition相关的代码,可以说,已经实现了前面提到的读写锁。但为什么加入condition呢?原因是这样的,如果有多个线程在连续进行读操作,可能导致长时间不能进行写操作,也就是通常说的写锁饥饿。condition可以解决这个问题,如果有线程正在等待写操作,那么新的读操作先等待,等到写操作完成后再开始。这样可以保证获得读写操作的机会是相对公平的。

在Windows中实现读写锁

  在Windows平台,Vista和Server 2008及其更高的版本才开始提供读写锁相关的API,如果需要支持XP系统,那么往往需要自己实现读写锁机制。前面已经介绍过如何实现读写锁,这里不费口舌,直接贴出代码

1 // 实现代码 2  3 class RWLock 4 { 5 public: 6     RWLock(); 7     ~RWLock(); 8 public: 9     void AcquireReadLock();10     void ReleaseReadLock();11     void AcquireWriteLock();12     void ReleaseWriteLock();13 private:14     volatile DWORD      m_cnt;15     CRITICAL_SECTION    m_cs;16     HANDLE              m_evt;17     HANDLE              m_sem;18 };19 20 RWLock::RWLock()21     : m_cnt(0)22     , m_evt(NULL)23     , m_cs(NULL)24     , m_sem(NULL)25 {26     // 提倡的做法在专门的初始化函数里创建和初始化这些变量27 28     ::InitializeCriticalSection(&m_cs);29 30     // Event必须是手动重置,否则存在死锁隐患,即等待Event前,先被激活了31     m_evt = ::CreateEvent(NULL, TRUE, TRUE, NULL);32     m_sem = ::CreateSemaphore(NULL, 1, 1, NULL);33 }34 35 RWLock::~RWLock()36 {37     ::CloseHandle(m_sem);38     ::CloseHandle(m_evt);39     ::DeleteCriticalSection(&m_cs);40 }41 42 void RWLock::AcquireReadLock()43 {44     ::WaitForSingleObject(m_evt, INFINITE);45 46     ::EnterCriticalSection(&m_cs);47     if(0 == m_cnt++)48         ::WaitForSingleObject(m_sem, INFINITE);49     ::LeaveCriticalSection(&m_cs);50 }51 52 void RWLock::ReleaseReadLock()53 {54     ::EnterCriticalSection(&m_cs);55     if(0 == --m_cnt)56         ::ReleaseSemaphore(m_sem, 1, NULL);57     ::LeaveCriticalSection(&m_cs);58 }59 60 void RWLock::AcquireWriteLock()61 {62     ::ResetEvent(m_evt);63     ::WaitForSingleObject(m_sem, INFINITE);64 }65 66 void RWLock::ReleaseWriteLock()67 {68     ::ReleaseSemaphore(m_sem, 1, NULL);69     ::SetEvent(m_evt);70 }71 72 // 使用示例73 74 void Read()75 {76     // 多个线程能够同时进行读操作77 78     rwLock.AcquireReadLock();79 80     // 读取数据81     82     rwLock.ReleaseReadLock();83 }84 85 void Write()86 {87     rwLock.AcquireWriteLock();88 89     // 写入数据90 91     rwLock.ReleaseWriteLock();92 }
View Code

 

PS:本文为博主的原创文章,请尊重博主辛苦码字的成果,转载请注明链接
 
 

转载于:https://www.cnblogs.com/coldberry/p/ReadWriteLock.html

你可能感兴趣的文章
习题答案-5
查看>>
使用pdo,使用pdo无法插入数据怎么办
查看>>
使用函数进行邮件发送的示例
查看>>
HDU 1231.最大连续子序列-dp+位置标记
查看>>
MessageDAL
查看>>
Orleans学习总结(一)--入门认识
查看>>
一种无限循环轮播图的实现原理
查看>>
P1219 八皇后
查看>>
算法一书配套网站
查看>>
《springcloud 二》微服务动态网关,网关集群
查看>>
网络基础之网络协议篇
查看>>
算法导论读书笔记(7)
查看>>
struts2.1笔记02:servlet简介
查看>>
前端分页功能的实现以及原理
查看>>
标准差分进化算法matlab程序实现(转载)
查看>>
leetcode[163]Missing Ranges
查看>>
前端面试题集锦
查看>>
php遍历文本文档txt文件中的链接内容为数组
查看>>
微信小程序
查看>>
C语言数据类型作业
查看>>