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

介绍

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

我可以使用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. Sliding Window Median

    Description Given an array of n integer, and a moving window(size k), move the window at each iterat ...

  2. A1113 | Integer Set Partition (25)

    太简单了 #include <stdio.h> #include <memory.h> #include <math.h> #include <string& ...

  3. day40

    session / cookies : 会话跟踪技术是Cookie与Session 会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话. Cookie通过在客户端记录信息确定用 ...

  4. GitHub 手把手教你如何把本地项目或代码提交到Github托管

    GitHub 手把手教你如何把项目或代码提交到Github托管 启动Git Bash命令行 重点内容 1.首先打开你的github,点击新建项目,点击new repositories ,然后直接给项目 ...

  5. Kubernetes中如何让Deployment更新镜像

    问题描述 我的deployment有单个pod,我的自定义docker镜像如下: 123 containers: - name: mycontainer image: myimage:latest 在 ...

  6. RNN 的不变性

    卷积神经网络的不变性  不变性的实现主要靠两点:大量数据(各种数据):网络结构(pooling)不变性的类型  1)平移不变性  卷积神经网络最初引入局部连接和空间共享,就是为了满足平移不变性.    ...

  7. JAVA | Java对象的内存分配过程是如何保证线程安全的?

    JAVA | Java对象的内存分配过程是如何保证线程安全的? 专注于Java领域优质技术,欢迎关注 作者 l Hollis 来源 l Hollis(ID:hollischuang) JVM内存结构, ...

  8. Java基础 awt Graphics2D 生成矩形图片并向其中画一条直线

        JDK :OpenJDK-11      OS :CentOS 7.6.1810      IDE :Eclipse 2019‑03 typesetting :Markdown   code ...

  9. Android之WebRTC介绍

    参考自:Introduction to WebRTC on AndroidAndroid之WebRTC介绍 WebRTC被誉为是web长期开源开发的一个新启元,是近年来web开发的最重要创新.WebR ...

  10. 泡泡一分钟:Context-Aware Modelling for Augmented Reality Display Behaviour

    张宁 Context-Aware Modelling for Augmented Reality Display Behaviour链接:https://pan.baidu.com/s/1RpX6kt ...