RWMutex:共享/专有的递归互斥锁
具有共享/独占访问权限,且具有升级/降级功能的互斥锁
介绍
我的目标是创建可以充当读/写锁定机制的对象。任何线程都可以锁定它以进行读取,但是只有一个线程可以锁定它以进行写入。在写入线程释放它之前,所有其他线程都将等待。在释放任何其他线程之前,写线程不会获取互斥体。
我可以使用Slim Reader / Writer锁,但是:
- 它们不是递归的,例如,
AcquireSRWLockExclusive()
如果同一线程较早调用同一函数,则对的调用将阻塞。 - 它们不可升级,例如,已将锁锁定为读取访问权限的线程无法将其锁定为写入操作。
- 它们不是可复制的句柄。
我可以尝试C ++ 14,shared_lock
但是我仍然需要C ++ 11支持。此外,我还不确定它是否可以真正满足我的要求。
因此,我不得不手动实现它。由于缺少,删除了普通的C ++ 11方法WaitForMultipleObjects (nyi)
。现在具有升级/降级功能。
RWMUTEX
这一节很简单。
class RWMUTEX
{
private:
HANDLE hChangeMap;
std::map<DWORD, HANDLE> Threads;
RWMUTEX(const RWMUTEX&) = delete;
RWMUTEX(RWMUTEX&&) = delete;
我需要std::map<DWORD,HANDLE>
为所有尝试访问共享资源的线程存储一个句柄,并且还需要一个互斥锁以确保对此映射的所有更改都是线程安全的。
构造函数
RWMUTEX(const RWMUTEX&) = delete;
void operator =(const RWMUTEX&) = delete; RWMUTEX()
{
hChangeMapWrite = CreateMutex(,,);
}
简单地创建一个映射互斥的句柄。对象不可复制。
创建
HANDLE CreateIf(bool KeepReaderLocked = false)
{
WaitForSingleObject(hChangeMap, INFINITE);
DWORD id = GetCurrentThreadId();
if (Threads[id] == )
{
HANDLE e0 = CreateMutex(, , );
Threads[id] = e0;
}
HANDLE e = Threads[id];
if (!KeepReaderLocked)
ReleaseMutex(hChangeMap);
return e;
}
当调用LockRead()或LockWrite()来锁定对象时,将调用这个私有函数。如果当前线程还没有将自己变为可能访问这个互斥锁的线程中,这个函数将为该线程创建一个互斥锁。如果其他线程已经锁定这个互斥对象进行写访问,那么这个函数就会阻塞,直到写线程释放这个对象为止。这个函数返回当前线程的互斥句柄。
锁定读取/释放读取
HANDLE LockRead()
{
auto f = CreateIf();
WaitForSingleObject(f,INFINITE);
return f;
}
void ReleaseRead(HANDLE f)
{
ReleaseMutex(f);
}
当您要锁定对象以进行读取访问并稍后释放它时,将调用这些函数。
锁/释放
void LockWrite()
{
CreateIf(true); // Wait for all
vector<HANDLE> AllThreads;
AllThreads.reserve(Threads.size());
for (auto& a : Threads)
{
AllThreads.push_back(a.second);
} WaitForMultipleObjects((DWORD)AllThreads.size(), AllThreads.data(), TRUE, INFINITE); // Reader is locked
} void ReleaseWrite()
{ // Release All
for (auto& a : Threads)
ReleaseMutex(a.second);
ReleaseMutex(hChangeMap);
}
当您希望锁定对象以进行写访问并在稍后释放它时,将调用这些函数。函数的作用是:
1.在锁期间没有注册新线程
2.任何读取线程都释放了锁
析构函数
RWMUTEX()
{
CloseHandle(hChangeMap);
hChangeMap = ;
for (auto& a : Threads)
CloseHandle(a.second);
Threads.clear();
}
析构函数确保清除所有句柄。
可升级/可升级锁
有时,您希望将读锁升级为写锁,而不先解锁,以提高效率。因此,LockWrite
被修改为:
void LockWrite(DWORD updThread = )
{
CreateIf(true); // Wait for all
AllThreads.reserve(Threads.size());
AllThreads.clear();
for (auto& a : Threads)
{
if (updThread == a.first) // except ourself if in upgrade operation
continue;
AllThreads.push_back(a.second);
}
auto tim = WaitForMultipleObjects((DWORD)AllThreads.size(), AllThreads.data(), TRUE, wi); if (tim == WAIT_TIMEOUT && wi != INFINITE)
OutputDebugString(L"LockWrite debug timeout!"); // We don't want to keep threads, the hChangeMap is enough
// We also release the handle to the upgraded thread, if any
for (auto& a : Threads)
ReleaseMutex(a.second); // Reader is locked
} void Upgrade()
{
LockWrite(GetCurrentThreadId());
} HANDLE Downgrade()
{
DWORD id = GetCurrentThreadId();
auto z = Threads[id];
auto tim = WaitForSingleObject(z, wi);
if (tim == WAIT_TIMEOUT && wi != INFINITE)
OutputDebugString(L"Downgrade debug timeout!");
ReleaseMutex(hChangeMap);
return z;
}
调用Upgrade()现在的结果是:
更改被锁定的映射
等待除我们自己的线程之外的所有读线程退出
然后我们释放我们自己的线程互斥锁,因为更改锁定的映射就足够了。
调用Downgrade()
结果:
- 直接从映射上获取手柄,无需重新锁定
- 锁定此句柄,就像我们处于读取模式一样
- 发布变更映射
因此,整个代码是(带有一些调试帮助):
// RWMUTEX
class RWMUTEX
{
private:
HANDLE hChangeMap = ;
std::map<DWORD, HANDLE> Threads;
DWORD wi = INFINITE;
RWMUTEX(const RWMUTEX&) = delete;
RWMUTEX(RWMUTEX&&) = delete;
operator=(const RWMUTEX&) = delete; public: RWMUTEX(bool D = false)
{
if (D)
wi = ;
else
wi = INFINITE;
hChangeMap = CreateMutex(, , );
} ~RWMUTEX()
{
CloseHandle(hChangeMap);
hChangeMap = ;
for (auto& a : Threads)
CloseHandle(a.second);
Threads.clear();
} HANDLE CreateIf(bool KeepReaderLocked = false)
{
auto tim = WaitForSingleObject(hChangeMap, INFINITE);
if (tim == WAIT_TIMEOUT && wi != INFINITE)
OutputDebugString(L"LockRead debug timeout!");
DWORD id = GetCurrentThreadId();
if (Threads[id] == )
{
HANDLE e0 = CreateMutex(, , );
Threads[id] = e0;
}
HANDLE e = Threads[id];
if (!KeepReaderLocked)
ReleaseMutex(hChangeMap);
return e;
} HANDLE LockRead()
{
auto z = CreateIf();
auto tim = WaitForSingleObject(z, wi);
if (tim == WAIT_TIMEOUT && wi != INFINITE)
OutputDebugString(L"LockRead debug timeout!");
return z;
} void LockWrite(DWORD updThread = )
{
CreateIf(true); // Wait for all
AllThreads.reserve(Threads.size());
AllThreads.clear();
for (auto& a : Threads)
{
if (updThread == a.first) // except ourself if in upgrade operation
continue;
AllThreads.push_back(a.second);
}
auto tim = WaitForMultipleObjects((DWORD)AllThreads.size(), AllThreads.data(), TRUE, wi); if (tim == WAIT_TIMEOUT && wi != INFINITE)
OutputDebugString(L"LockWrite debug timeout!"); // We don't want to keep threads, the hChangeMap is enough
// We also release the handle to the upgraded thread, if any
for (auto& a : Threads)
ReleaseMutex(a.second); // Reader is locked
} void ReleaseWrite()
{
ReleaseMutex(hChangeMap);
} void ReleaseRead(HANDLE f)
{
ReleaseMutex(f);
} void Upgrade()
{
LockWrite(GetCurrentThreadId());
} HANDLE Downgrade()
{
DWORD id = GetCurrentThreadId();
auto z = Threads[id];
auto tim = WaitForSingleObject(z, wi);
if (tim == WAIT_TIMEOUT && wi != INFINITE)
OutputDebugString(L"Downgrade debug timeout!");
ReleaseMutex(hChangeMap);
return z;
}
};
要使用RWMUTEX
,可以简单地创建锁定类:
class RWMUTEXLOCKREAD
{
private:
RWMUTEX* mm = ;
public: RWMUTEXLOCKREAD(const RWMUTEXLOCKREAD&) = delete;
void operator =(const RWMUTEXLOCKREAD&) = delete; RWMUTEXLOCKREAD(RWMUTEX*m)
{
if (m)
{
mm = m;
mm->LockRead();
}
}
~RWMUTEXLOCKREAD()
{
if (mm)
{
mm->ReleaseRead();
mm = ;
}
}
}; class RWMUTEXLOCKWRITE
{
private:
RWMUTEX* mm = ;
public:
RWMUTEXLOCKWRITE(RWMUTEX*m)
{
if (m)
{
mm = m;
mm->LockWrite();
}
}
~RWMUTEXLOCKWRITE()
{
if (mm)
{
mm->ReleaseWrite();
mm = ;
}
}
};
还有一个用于升级机制的新类:
class RWMUTEXLOCKREADWRITE
{
private:
RWMUTEX* mm = ;
HANDLE lm = ;
bool U = false;
public: RWMUTEXLOCKREADWRITE(const RWMUTEXLOCKREADWRITE&) = delete;
void operator =(const RWMUTEXLOCKREADWRITE&) = delete; RWMUTEXLOCKREADWRITE(RWMUTEX*m)
{
if (m)
{
mm = m;
lm = mm->LockRead();
}
} void Upgrade()
{
if (mm && !U)
{
mm->Upgrade();
lm = ;
U = ;
}
} void Downgrade()
{
if (mm && U)
{
lm = mm->Downgrade();
U = ;
}
} ~RWMUTEXLOCKREADWRITE()
{
if (mm)
{
if (U)
mm->ReleaseWrite();
else
mm->ReleaseRead(lm);
lm = ;
mm = ;
}
}
};
用法示例:
RWMUTEX m; // ... other code
void foo1() {
RWMUTEXLOCKREAD lock(&m);
} void foo2() {
RWMUTEXLOCKWRITE lock(&m);
}
RWMutex:共享/专有的递归互斥锁的更多相关文章
- Go 互斥锁(sync.Mutex)和 读写锁(sync.RWMutex)
什么时候需要用到锁? 当程序中就一个线程的时候,是不需要加锁的,但是通常实际的代码不会只是单线程,所以这个时候就需要用到锁了,那么关于锁的使用场景主要涉及到哪些呢? 多个线程在读相同的数据时 多个线程 ...
- go Mutex (互斥锁)和RWMutex(读写锁)
转载自: https://blog.csdn.net/skh2015java/article/details/60334437 golang中sync包实现了两种锁Mutex (互斥锁)和RWMute ...
- linux c学习笔记----互斥锁属性
转自:http://lobert.iteye.com/blog/1762844 互斥锁属性 使用互斥锁(互斥)可以使线程按顺序执行.通常,互斥锁通过确保一次只有一个线程执行代码的临界段来同步多个线程. ...
- 四十、Linux 线程——互斥锁和读写锁
40.1 互斥锁 40.1.1 介绍 互斥锁(mutex)是一种简单的加锁的方法来控制对共享资源的访问. 在同一时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源进行访问. 若其他线程 ...
- C11线程管理:互斥锁
1.概述 锁类型 c11提供了跨平台的线程同步手段,用来保护多线程同时访问的共享数据. std::mutex,最基本的 Mutex 类,独占的互斥量,不能递归使用. std::time_mutex,带 ...
- 【Linux C 多线程编程】互斥锁与条件变量
一.互斥锁 互斥量从本质上说就是一把锁, 提供对共享资源的保护访问. 1) 初始化: 在Linux下, 线程的互斥量数据类型是pthread_mutex_t. 在使用前, 要对它进行初始化: 对于静态 ...
- Linux互斥锁、条件变量和信号量
Linux互斥锁.条件变量和信号量 来自http://kongweile.iteye.com/blog/1155490 http://www.cnblogs.com/qingxia/archive/ ...
- 【linux】系统编程-6-POSIX标准下的信号量与互斥锁
目录 前言 8. POSIX信号量 8.1 概念 8.2 POSIX无名信号量 8.3 POSIX有名信号量 8.4 POPSIX信号量与system V信号量的区别 9. POSIX互斥锁 9.1 ...
- 【多线程】C++ 互斥锁(mutex)的简单原理分析
多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序.一般情况下,分为两种类型的多任务处理:基于进程和基于线程. 1)基于进程的多任务处理是程序的并发执行. 2)基于线程 ...
随机推荐
- Scrapy爬虫案例 | 数据存储至MySQL
首先,MySQL创建好数据库和表 然后编写各个模块 item.py import scrapy class JianliItem(scrapy.Item): name = scrapy.Field() ...
- 使用docker部署微服务
https://my.oschina.net/silenceyawen/blog/1819472 http://jvm123.com/2019/08/docker-shi-yong.html 从201 ...
- 统计学基础知识(二)---推断统计学(Inferential Statistics)
推断统计学(Inferential Statistics):利用样本信息对总体进行估计和假设检验. 总体(population):在一个特定研究中所有感兴趣的个体组成的集合. 样本(sample):总 ...
- spring注解式参数校验列表
校验注释列表: @AssertFalse Boolean,boolean 验证注解的元素值是false @AssertTrue Boolean,boolean 验证注解的元素值是true @NotNu ...
- MyBatis框架,增删改查
一.recourses中核心配置文件mybatis-config.xml 二. recourse中jdbc.properties 三.entity实体类 四.Dao层 五.ISmbmsUserDao. ...
- HustOJ二次开发之修改数据库连接池
有的时候我们会因为某种业务需要的情况下,需要修改hustoj默认的数据库连接池之类的. 修改数据库连接池步骤 进入到对应的目录 /home/judge/src/web/include 找到db_inf ...
- Waring:not all local change may be shown due to an error:fatal
idea往Git上提交文件时提示 Waring:not all local change may be shown due to an error:fatal 解决方案:选择File --> s ...
- vmware vsphere各版本差别,及各套件差别
最近要开始全面支持虚拟化了,客户私有云环境用的多的为vmware vsphere,特地恶补下vmware vsphere的各个差别. 首先是vSphere,ESXi和vCenter 的区别. ESXi ...
- Deepin系统中手动开启swap的方法
Deepin系统中手动开启swap的方法 如何设置 swap(交换空间)的大小建议设置和你的实际物理内存一样大,如你的内存是8G的,则可将下面的count的值设为8192(当然这只是参考值,你可根据你 ...
- 运维笔记--Docker环境ubuntu系统安装指定版本python[3.6]
场景描述: 直接安装出现如下异常: root@ae2d02e458f3:/home# apt-get install python3.6 Reading package lists... Done B ...