具有共享/独占访问权限,且具有升级/降级功能的互斥锁

介绍

我的目标是创建可以充当读/写锁定机制的对象。任何线程都可以锁定它以进行读取,但是只有一个线程可以锁定它以进行写入。在写入线程释放它之前,所有其他线程都将等待。在释放任何其他线程之前,写线程不会获取互斥体。

我可以使用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:共享/专有的递归互斥锁的更多相关文章

  1. Go 互斥锁(sync.Mutex)和 读写锁(sync.RWMutex)

    什么时候需要用到锁? 当程序中就一个线程的时候,是不需要加锁的,但是通常实际的代码不会只是单线程,所以这个时候就需要用到锁了,那么关于锁的使用场景主要涉及到哪些呢? 多个线程在读相同的数据时 多个线程 ...

  2. go Mutex (互斥锁)和RWMutex(读写锁)

    转载自: https://blog.csdn.net/skh2015java/article/details/60334437 golang中sync包实现了两种锁Mutex (互斥锁)和RWMute ...

  3. linux c学习笔记----互斥锁属性

    转自:http://lobert.iteye.com/blog/1762844 互斥锁属性 使用互斥锁(互斥)可以使线程按顺序执行.通常,互斥锁通过确保一次只有一个线程执行代码的临界段来同步多个线程. ...

  4. 四十、Linux 线程——互斥锁和读写锁

    40.1 互斥锁 40.1.1 介绍 互斥锁(mutex)是一种简单的加锁的方法来控制对共享资源的访问. 在同一时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源进行访问. 若其他线程 ...

  5. C11线程管理:互斥锁

    1.概述 锁类型 c11提供了跨平台的线程同步手段,用来保护多线程同时访问的共享数据. std::mutex,最基本的 Mutex 类,独占的互斥量,不能递归使用. std::time_mutex,带 ...

  6. 【Linux C 多线程编程】互斥锁与条件变量

    一.互斥锁 互斥量从本质上说就是一把锁, 提供对共享资源的保护访问. 1) 初始化: 在Linux下, 线程的互斥量数据类型是pthread_mutex_t. 在使用前, 要对它进行初始化: 对于静态 ...

  7. Linux互斥锁、条件变量和信号量

    Linux互斥锁.条件变量和信号量  来自http://kongweile.iteye.com/blog/1155490 http://www.cnblogs.com/qingxia/archive/ ...

  8. 【linux】系统编程-6-POSIX标准下的信号量与互斥锁

    目录 前言 8. POSIX信号量 8.1 概念 8.2 POSIX无名信号量 8.3 POSIX有名信号量 8.4 POPSIX信号量与system V信号量的区别 9. POSIX互斥锁 9.1 ...

  9. 【多线程】C++ 互斥锁(mutex)的简单原理分析

    多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序.一般情况下,分为两种类型的多任务处理:基于进程和基于线程. 1)基于进程的多任务处理是程序的并发执行. 2)基于线程 ...

随机推荐

  1. vim文本编辑器——替换、保存退出

    1.替换: (1)全文替换: 利用查询命令查询: (2)指定替换的字符串的范围: 2.保存.退出命令: (1)在命令行模式下保存(:w) (2)另存为(:w+要保存的文件的路径) (3)保存退出(:w ...

  2. Hibernate的级联保存、级联删除

    级联操作: 属性:cascade 值:save-update(级联保存) delete(级联删除) all(级联保存+级联删除) 优点:虽然,不用级联操作也能解决问题.但是级联操作可以减少代码量,使得 ...

  3. Linux后台开发工具箱-葵花宝典

    Linux后台开发工具箱-葵花宝典 一见 2016/11/4 目录 目录 1 1. 前言 4 2. 脚本类工具 4 2.1. 双引号和单引号 4 2.2. 取脚本完整文件路径 5 2.3. 环境变量和 ...

  4. 洛谷p2330繁忙的都市题解

    题面 根据题意来分析, 要求出你选了几条路, 最小生成树是能解的, 那么就直接输出n - 1条路即可, 至于最大值则走一遍最小生成树求出即可 这里提供最小生成树的两种方法 1. 克鲁斯卡尔 克鲁斯卡尔 ...

  5. sharding-jdbc使用笔记

    核心概念: 1.逻辑表:水平拆分的数据库(表)的相同逻辑和数据结构表的总称.例,根据主键尾数拆分为10张表,分别是t_order_0到t_order_9,他们的逻辑表名为t_order 2.真实表:在 ...

  6. 前端零基础入门:页面结构层HTML(2)

    学习笔记 若本号内容有做得不到位的地方(比如:涉及版权或其他问题),请及时联系我们进行整改即可,会在第一时间进行处理. 请点赞!因为你们的赞同/鼓励是我写作的最大动力! 欢迎关注达达的简书! 这是一个 ...

  7. 使用helm进行kubernetes包管理

    1. 安装helm package https://github.com/helm/helm/blob/master/LICENSE 2. 将 helm 配置到环境变量 3. 使用helm的前提是安装 ...

  8. php如何实现三级分销

    Q: 项目要实现三级分销;对于数据库的设计和用户注册后给所有上级(最多三级)返利 但是一点头绪都没有,请大神帮忙给个思路! 如果是直接注册给奖励20元如果是通过二维码或者链接进入的注册页面 找到上级 ...

  9. Tensorflows安装(cpu版安装方法)

    一.说明 首先声明,本人系统是Windows10 64位,Win7未试. 本文旨在帮助园友以更简单的方式安装Tensorflow,下面介绍的是如何安装Python的Tensorflow cpu版本. ...

  10. Eclipse安装代码反编译插件Enhanced Class Decompiler

    在开发过程中,如果想查看引入资源的源代码,可以借助eclipse的插件Enhanced Class Decompiler轻松实现,下面我来讲解一下如何安装使用这个插件. 1.打开Eclipse菜单-& ...