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

介绍

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

我可以使用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. read函数和write函数

    read 函数 inline int read() { ,f=; char c=getchar(); ; c=getchar();} +c-'; c=getchar();} return f*x; } ...

  2. 02_搭建HTTP客户端与服务器(数通华为)

    1.选择客户端和服务端: 2.服务端创建HTTP目录: 3.客户端访问下载:

  3. 文件夹上传控件webupload插件

    我们平时经常做的是上传文件,上传文件夹与上传文件类似,但也有一些不同之处,这次做了上传文件夹就记录下以备后用. 这次项目的需求: 支持大文件的上传和续传,要求续传支持所有浏览器,包括ie6,ie7,i ...

  4. 【POJ3278】Catch That Cow

    本题传送门 本题知识点:宽度优先搜索 题意很简单,我们把FJ与奶牛看作是在一条数轴上的点,奶牛固定在K点,FJ的移动有三种,分别是向前一格,向后一格以及跳到当前格的两倍去.问FJ花费最少的时间到达奶牛 ...

  5. 【BigData】Java基础_循环

    1.for循环 语法: for (初始表达式;布尔表达式;步进) { 循环体: } 实例: package cn.test.logan.day02; import java.util.Scanner; ...

  6. 记一次cpu指标异常的跟踪排查

    问题描述: 最近在测试环境的服务器上,无意中发现cpu持续飙高.最高的时候达到了200%经过反复重启无效之后,决定挖掘深层次的原因 通过top命令打印出消耗cpu的pid,如图 通过ps -mp 24 ...

  7. [asm] 小菜汇编基础和学习技巧小结(一)

    以下小结纯属小菜自学过程产生的dump,大神请飘过! 汇编是一门庞大复杂的学问,在计算机的世界里差不多无所不入.很多编程领域都会或多或少跟汇编打交道.本人不是科班出身的程序员,所以很多基础都为零,学历 ...

  8. Python中如何计算字符串里面某一个字符出现的次数?

    一个突发奇想,想解决一个学习中的行业痛点,让我又再度拾起了Python编程语言的学习.   刚学两天,今天遇到一个题,该题难度不高,但有一点关键点在网上找不到,网上也没有相关的答案,于是我只好千辛万苦 ...

  9. 后台启动es head,关闭shell后es head自动关闭

    后台启动head命令:grunt server & 注意:加上&虽然执行了后台启动,但还是有日志打印出来,使用ctrl+c可以退出.这时如果直接关闭shell, head进程就会终止 ...

  10. session与getSession()用法总结

    一.session 1.session的过期时间是从什么时候开始计算的?是从一登录就开始计算还是说从停止活动开始计算? 从session不活动的时候开始计算,如果session一直活动,session ...