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

介绍

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

我可以使用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. php实现隔行换色

    <?php $i = 0 ; //声明一个变量 echo "<table width='800' border='1px'>"; //表格开头 设置宽度边框 wh ...

  2. 洛谷 P2872 【[USACO07DEC]道路建设Building Roads】

    P2872 传送门 首先 题目概括:题目让着求使所有牧场都联通.需要修建多长的路. 显然这是一道最小生成树板子题(推荐初学者做). 那我就说一下kruskal吧. Kruskal算法是一种用来查找最小 ...

  3. Unknown column 'startname' in 'field list

    Unknown column 'startname' in 'field list  字段匹配不上 解决思路 1.检查数据库字段名与sql中的字段名是否一致 2.是否为关键字或中英文区别 ,(关键字可 ...

  4. BERT模型

    BERT模型是什么 BERT的全称是Bidirectional Encoder Representation from Transformers,即双向Transformer的Encoder,因为de ...

  5. js控制网页窗口一打开就自动全屏

    1.如果不需要开新窗口 在body区加入: <body   onLoad= "javascript:window.resizeTo(screen.availWidth,screen.a ...

  6. uniapp - emmet

    话说,emment是官方uniapp直接引入的.基本上没做啥修改:可以点这里查看所有用法 - http://emmet.evget.com/ 1.类似css层级写法 1.1 view.ok>vi ...

  7. poi导出word表格

    代码如下: package com.ksource.pwlp.util; import java.io.FileOutputStream; import java.math.BigInteger; i ...

  8. MySQL与MariaDB核心特性比较详细版v1.0(覆盖mysql 8.0/mariadb 10.3,包括优化、功能及维护)

    注:本文严禁任何形式的转载,原文使用word编写,为了大家阅读方便,提供pdf版下载. MySQL与MariaDB主要特性比较详细版v1.0(不含HA).pdf 链接:https://pan.baid ...

  9. Java基础 awt Frame 设置窗体的大小 位置 可见性

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

  10. Spring Web Flux 相关概念

    Reactive Streams.Reactor 和 Web Flux 上面介绍了反应式编程的一些概念,以及 Reactor 和 Web Flux.可能读者看到这里有些乱.这里介绍一下三者的关系.其实 ...