C++ 通用锁管理
lock_guard
类 lock_guard 是互斥体包装器,为在作用域块期间占有互斥提供便利 RAII 风格机制。
创建 lock_guard 对象时,它试图接收给定互斥的所有权。控制离开创建 lock_guard 对象的作用域时,销毁 lock_guard 并释放互斥。
lock_guard 类不可复制。
成员类型
	mutex_type	Mutex成员函数
成员函数
	(构造函数)	构造 lock_guard ,可选地锁定给定的互斥(公开成员函数)
	(析构函数)	析构 lock_guard 对象,解锁底层互斥(公开成员函数)
	operator=[被删除]	不可复制赋值(公开成员函数)
#include <thread>
#include <mutex>
#include <iostream>
int g_i = 0;
std::mutex g_i_mutex;  // 保护 g_i
void safe_increment()
{
    std::lock_guard<std::mutex> lock(g_i_mutex);
    ++g_i;
    std::cout << std::this_thread::get_id() << ": " << g_i << '\n';
    // g_i_mutex 在锁离开作用域时自动释放
}
int main()
{
    std::cout << "main: " << g_i << '\n';
    std::thread t1(safe_increment);
    std::thread t2(safe_increment);
    t1.join();
    t2.join();
    std::cout << "main: " << g_i << '\n';
}
可能的输出:
main: 0
140641306900224: 1
140641298507520: 2
main: 2
scoped_lock
类 scoped_lock 是提供便利 RAII 风格机制的互斥包装器,它在作用域块的存在期间占有一或多个互斥。
创建 scoped_lock 对象时,它试图取得给定互斥的所有权。控制离开创建 scoped_lock 对象的作用域时,析构 scoped_lock 并释放互斥。若给出数个互斥,则使用免死锁算法,如同以 std::lock 。
scoped_lock 类不可复制。
成员类型
	mutex_type (若 sizeof...(MutexTypes)==1)	Mutex , MutexTypes... 中的单独类型
成员函数
	(构造函数)	构造 scoped_lock ,可选地锁定给定的互斥(公开成员函数)
	(析构函数) 	析构 scoped_lock 对象,解锁底层互斥(公开成员函数)
	operator=[被删除]	不可复制(公开成员函数)
#include <mutex>
#include <thread>
#include <iostream>
#include <vector>
#include <functional>
#include <chrono>
#include <string>
struct Employee
{
    Employee(std::string id) : id(id) {}
    std::string id;
    std::vector<std::string> lunch_partners;
    std::mutex m;
    std::string output() const
    {
        std::string ret = "Employee " + id + " has lunch partners: ";
        for (const auto &partner : lunch_partners)
            ret += partner + " ";
        return ret;
    }
};
void send_mail(Employee &, Employee &)
{
    // 模拟耗时的发信操作
    std::this_thread::sleep_for(std::chrono::seconds(1));
}
void assign_lunch_partner(Employee &e1, Employee &e2)
{
    static std::mutex io_mutex;
    {
        std::lock_guard<std::mutex> lk(io_mutex);
        std::cout << e1.id << " and " << e2.id << " are waiting for locks" << std::endl;
    }
    {
        // 用 std::scoped_lock 取得二个锁,而无需担心
        // 其他对 assign_lunch_partner 的调用死锁我们
        // 而且它亦提供便利的 RAII 风格机制
        std::scoped_lock lock(e1.m, e2.m);
        // 等价代码 1 (用 std::lock 和 std::lock_guard )
        // std::lock(e1.m, e2.m);
        // std::lock_guard<std::mutex> lk1(e1.m, std::adopt_lock);
        // std::lock_guard<std::mutex> lk2(e2.m, std::adopt_lock);
        // 等价代码 2 (若需要 unique_lock ,例如对于条件变量)
        // std::unique_lock<std::mutex> lk1(e1.m, std::defer_lock);
        // std::unique_lock<std::mutex> lk2(e2.m, std::defer_lock);
        // std::lock(lk1, lk2);
        {
            std::lock_guard<std::mutex> lk(io_mutex);
            std::cout << e1.id << " and " << e2.id << " got locks" << std::endl;
        }
        e1.lunch_partners.push_back(e2.id);
        e2.lunch_partners.push_back(e1.id);
    }
    send_mail(e1, e2);
    send_mail(e2, e1);
}
int main()
{
    Employee alice("alice"), bob("bob"), christina("christina"), dave("dave");
    // 在并行线程中指派,因为就午餐指派发邮件消耗很长时间
    std::vector<std::thread> threads;
    threads.emplace_back(assign_lunch_partner, std::ref(alice), std::ref(bob));
    threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(bob));
    threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(alice));
    threads.emplace_back(assign_lunch_partner, std::ref(dave), std::ref(bob));
    for (auto &thread : threads)
        thread.join();
    std::cout << alice.output() << '\n'
              << bob.output() << '\n'
              << christina.output() << '\n'
              << dave.output() << '\n';
}
可能的输出:
alice and bob are waiting for locks
alice and bob got locks
christina and bob are waiting for locks
christina and alice are waiting for locks
dave and bob are waiting for locks
dave and bob got locks
christina and alice got locks
christina and bob got locks
Employee alice has lunch partners: bob christina
Employee bob has lunch partners: alice dave christina
Employee christina has lunch partners: alice bob
Employee dave has lunch partners: bob
unique_lock
类 unique_lock 是通用互斥包装器,允许延迟锁定、锁定的有时限尝试、递归锁定、所有权转移和与条件变量一同使用。
类 unique_lock 可移动,但不可复制——它满足可移动构造 (MoveConstructible) 和可移动赋值 (MoveAssignable) 但不满足可复制构造 (CopyConstructible) 或可复制赋值 (CopyAssignable) 。
类 unique_lock 满足基本可锁定 (BasicLockable) 要求。若 Mutex 满足可锁定 (Lockable) 要求,则 unique_lock 亦满足可锁定 (Lockable) 要求(例如:能用于 std::lock ) ;若 Mutex 满足可定时锁定 (TimedLockable) 要求,则 unique_lock 亦满足可定时锁定 (TimedLockable) 要求。
成员类型
	mutex_type	Mutex
成员函数
	(构造函数)	构造 unique_lock ,可选地锁定提供的互斥(公开成员函数)
	(析构函数)	若占有关联互斥,则解锁之(公开成员函数)
	operator=	若占有则解锁互斥,并取得另一者的所有权(公开成员函数)
锁定
	lock	锁定关联互斥(公开成员函数)
	try_lock	尝试锁定关联互斥,若互斥不可用则返回(公开成员函数)
	try_lock_for	试图锁定关联的可定时锁定 (TimedLockable) 互斥,若互斥在给定时长中不可用则返回(公开成员函数)
	try_lock_until	尝试锁定关联可定时锁定 (TimedLockable) 互斥,若抵达指定时间点互斥仍不可用则返回(公开成员函数)
	unlock	解锁关联互斥(公开成员函数)
修改器
	swap	与另一 std::unique_lock 交换状态(公开成员函数)
	release	将关联互斥解关联而不解锁它(公开成员函数)
观察器
	mutex	返回指向关联互斥的指针(公开成员函数)
	owns_lock	测试锁是否占有其关联互斥(公开成员函数)
	operator bool	测试锁是否占有其关联互斥(公开成员函数)非成员函数
	std::swap(std::unique_lock) (C++11)	std::swap 对 unique_lock 的特化(函数模板)
#include <mutex>
#include <thread>
#include <chrono>
struct Box {
    explicit Box(int num) : num_things{num} {}
    int num_things;
    std::mutex m;
};
void transfer(Box &from, Box &to, int num)
{
    // 仍未实际取锁
    std::unique_lock<std::mutex> lock1(from.m, std::defer_lock);
    std::unique_lock<std::mutex> lock2(to.m, std::defer_lock);
    // 锁两个 unique_lock 而不死锁
    std::lock(lock1, lock2);
    from.num_things -= num;
    to.num_things += num;
    // 'from.m' 与 'to.m' 互斥解锁于 'unique_lock' 析构函数
}
int main()
{
    Box acc1(100);
    Box acc2(50);
    std::thread t1(transfer, std::ref(acc1), std::ref(acc2), 10);
    std::thread t2(transfer, std::ref(acc2), std::ref(acc1), 5);
    t1.join();
    t2.join();
}
shared_lock
类 shared_lock 是通用共享互斥所有权包装器,允许延迟锁定、定时锁定和锁所有权的转移。锁定 shared_lock ,会以共享模式锁定关联的共享互斥( std::unique_lock 可用于以排他性模式锁定)。
shared_lock 类可移动,但不可复制——它满足可移动构造 (MoveConstructible) 与可移动赋值 (MoveAssignable) 的要求,但不满足可复制构造 (CopyConstructible) 或可复制赋值 (CopyAssignable) 。
shared_lock 符合可锁定 (Lockable) 要求。若 Mutex 符合可共享定时锁定 (SharedTimedLockable) 要求,则 shared_lock 亦符合 可定时锁定 (TimedLockable) 要求。
为以共享所有权模式等待于共享互斥,可使用 std::condition_variable_any ( std::condition_variable 要求 std::unique_lock 故而只能以唯一所有权模式等待)。
成员类型
	mutex_type	Mutex
成员函数
	(构造函数)	构造 shared_lock ,可选地锁定提供的互斥(公开成员函数)
	(析构函数)	解锁关联的互斥(公开成员函数)
	operator=	若占有则解锁互斥,然后获得对方的所有权(公开成员函数)
共享锁定
	lock	锁定关联的互斥(公开成员函数)
	try_lock	尝试锁定关联的互斥(公开成员函数)
	try_lock_for	尝试锁定关联的互斥,以指定时长(公开成员函数)
	try_lock_until	尝试锁定关联的互斥,直至指定的时间点(公开成员函数)
	unlock	解锁关联的互斥(公开成员函数)
修改器
	swap	与另一 shared_lock 交换数据成员(公开成员函数)
	release	解除关联 mutex 而不解锁(公开成员函数)
观察器
	mutex	返回指向关联的互斥的指针(公开成员函数)
	owns_lock	测试锁是否占有其关联的互斥(公开成员函数)
	operator bool	测试锁是否占有其关联的互斥(公开成员函数)
非成员函数
	std::swap(std::shared_lock)(C++14)	std::swap 对 shared_lock 的特化(函数模板)
lock
锁定给定的可锁定 (Lockable) 对象 lock1 、 lock2 、 ... 、 lockn ,用免死锁算法避免死锁。
以对 lock 、 try_lock 和 unlock 的未指定系列调用锁定对象。若调用 lock 或 unlock 导致异常,则在重抛前对任何已锁的对象调用 unlock 。
#include <mutex>
#include <thread>
#include <iostream>
#include <vector>
#include <functional>
#include <chrono>
#include <string>
struct Employee {
    Employee(std::string id) : id(id) {}
    std::string id;
    std::vector<std::string> lunch_partners;
    std::mutex m;
    std::string output() const
    {
        std::string ret = "Employee " + id + " has lunch partners: ";
        for( const auto& partner : lunch_partners )
            ret += partner + " ";
        return ret;
    }
};
void send_mail(Employee &, Employee &)
{
    // 模拟耗时的发信操作
    std::this_thread::sleep_for(std::chrono::seconds(1));
}
void assign_lunch_partner(Employee &e1, Employee &e2)
{
    static std::mutex io_mutex;
    {
        std::lock_guard<std::mutex> lk(io_mutex);
        std::cout << e1.id << " and " << e2.id << " are waiting for locks" << std::endl;
    }
    // 用 std::lock 获得二个锁,而不担心对 assign_lunch_partner 的其他调用会死锁我们
    {
        std::lock(e1.m, e2.m);
        std::lock_guard<std::mutex> lk1(e1.m, std::adopt_lock);
        std::lock_guard<std::mutex> lk2(e2.m, std::adopt_lock);
// 等价代码(若需要 unique_locks ,例如对于条件变量)
//        std::unique_lock<std::mutex> lk1(e1.m, std::defer_lock);
//        std::unique_lock<std::mutex> lk2(e2.m, std::defer_lock);
//        std::lock(lk1, lk2);
// C++17 中可用的较优解法
//        std::scoped_lock lk(e1.m, e2.m);
        {
            std::lock_guard<std::mutex> lk(io_mutex);
            std::cout << e1.id << " and " << e2.id << " got locks" << std::endl;
        }
        e1.lunch_partners.push_back(e2.id);
        e2.lunch_partners.push_back(e1.id);
    }
    send_mail(e1, e2);
    send_mail(e2, e1);
}
int main()
{
    Employee alice("alice"), bob("bob"), christina("christina"), dave("dave");
    // 在平行线程指派,因为发邮件给用户告知午餐指派,会消耗长时间
    std::vector<std::thread> threads;
    threads.emplace_back(assign_lunch_partner, std::ref(alice), std::ref(bob));
    threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(bob));
    threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(alice));
    threads.emplace_back(assign_lunch_partner, std::ref(dave), std::ref(bob));
    for (auto &thread : threads) thread.join();
    std::cout << alice.output() << '\n'  << bob.output() << '\n'
              << christina.output() << '\n' << dave.output() << '\n';
}
可能的输出:
alice and bob are waiting for locks
alice and bob got locks
christina and bob are waiting for locks
christina and bob got locks
christina and alice are waiting for locks
christina and alice got locks
dave and bob are waiting for locks
dave and bob got locks
Employee alice has lunch partners: bob christina
Employee bob has lunch partners: alice christina dave
Employee christina has lunch partners: bob alice
Employee dave has lunch partners: bob
std::defer_lock, std::try_to_lock, std::adopt_lock
std::lock_guard的std::adopt_lock参数
- 加入adopt_lock后,在调用lock_guard的构造函数时,不再进行lock();
 - adopt_guard为结构体对象,起一个标记作用,表示这个互斥量已经lock(),不需要在lock()。
 
unique_lock的第二个参数std::adopt_lock:
- 表示这个互斥量已经被lock(),即不需要在构造函数中lock这个互斥量了。
 - 前提:必须提前lock
 
std::try_to_lock:
- 尝试用mutex的lock()去锁定这个mutex,但如果没有锁定成功,会立即返回,不会阻塞在那里;
 - 使用try_to_lock的原因是防止其他的线程锁定mutex太长时间,导致本线程一直阻塞在lock这个地方
 - 前提:不能提前lock();
 - owns_locks()方法判断是否拿到锁,如拿到返回true
 
std::defer_lock:
- 如果没有第二个参数就对mutex进行加锁,加上defer_lock是始化了一个没有加锁的mutex
 - 不给它加锁的目的是以后可以调用unique_lock的一些方法
 - 前提:不能提前lock
 
call_once
函数模板,该函数的第一个参数为标记,第二个参数是一个函数名。
功能:能够保证函数只被调用一次。具备互斥量的能力,而且比互斥量消耗的资源更少,更高效。
call_once()需要与一个标记结合使用,这个标记为std::once_flag;其实once_flag是一个结构,call_once()就是通过标记来决定函数是否执行,调用成功后,就把标记设置为一种已调用状态。
#include <iostream>
#include <thread>
#include <mutex>
std::once_flag flag1, flag2;
void simple_do_once()
{
    std::call_once(flag1, []()
                   { std::cout << "Simple example: called once\n"; });
}
void may_throw_function(bool do_throw)
{
    if (do_throw)
    {
        std::cout << "throw: call_once will retry\n";
        // throw std::exception();
    }
    std::cout << "Didn't throw, call_once will not attempt again\n";
}
void do_once(bool do_throw)
{
    try
    {
        std::call_once(flag2, may_throw_function, do_throw);
    }
    catch (...)
    {
    }
}
int main()
{
    std::thread st1(simple_do_once);
    std::thread st2(simple_do_once);
    std::thread st3(simple_do_once);
    st1.join();
    st2.join();
    st3.join();
    std::thread t1(do_once, true);
    std::thread t2(do_once, false);
    std::thread t3(do_once, true);
    std::thread t4(do_once, true);
    t1.join();
    t2.join();
    t3.join();
    t4.join();
}
可能会是:
Simple example: called once
throw: call_once will retry
Didn't throw, call_once will not attempt again
												
											C++ 通用锁管理的更多相关文章
- 解析大型.NET ERP系统 通用附件管理功能
		
大型系统具备一个通用的附件管理功能,对于单据中无法清晰表达的字段,用一个附件图片或附件文档表示是最好的方法了.比如物料清单附加一张CAD图纸,销售订单评审功能中附加客户的各种表格,通用附件功能对系统起 ...
 - winform快速开发平台 -> 通用权限管理之动态菜单
		
这几个月一直忙APP的项目,没来得及更新项目,想想该抽出时间整理一下开发思路,跟大家分享,同时也希望得到宝贵的建议. 先说一下我们的权限管理的的设计思路,首先一个企业信息化管理系统一定会用到权限管理, ...
 - 基于吉日嘎拉的通用权限管理Webform版老界面bug修复
		
虽然弄了新界面<基于吉日嘎底层架构的通用权限管理Web端UI更新:参考DTcms后台界面>,但老界面的一点菜单显示的问题还是让我这种强迫症揪心,终于今晚可以美美的睡觉了. 老代码用了Ses ...
 - 基于吉日嘎底层架构的通用权限管理Web端UI更新:参考DTcms后台界面
		
经一周的研究学习,看了国内的H+.HUI等,国外的PaperDashboardPro.Make.Metronic BootStrap等,最终选定用一个轻量的,适合中国人的,来自DTcms的后台管理UI ...
 - 【原创】C#通用权限管理-程序安全检查,这些你一定要考虑到位
		
接触通用权限已经一年,现在使用已经很熟练,分享通用权限管理下面的一些好的开发思想. 安全漏洞对于一个小项目来说,可能不是特别的重视,对于一个大项目来说,这是特别重要需要注意的,特别是在项目开发中的就要 ...
 - EASYUI+MVC4通用权限管理平台--前言
		
经过多年的管理信息系统的开发工作,吸取了工作中遇到的一些问题,经过自己的总结,形成了一套比较完整的管理信息系统的通用权限管理基础开发平台. 在软件的开发过程中我们首先需要解决的是UI问题,其次是浏览器 ...
 - EASYUI+MVC4通用权限管理平台
		
通用权限案例平台在经过几年的实际项目使用,并取得了不错的用户好评.在平台开发完成后,特抽空总结一下平台知识,请各位在以后的时间里,关注博客的更新. 1.EASYUI+MVC4通用权限管理平台--前言 ...
 - C#通用权限管理-程序安全检查,这些你一定要考虑到位
		
接触通用权限已经一年,现在使用已经很熟练,分享通用权限管理下面的一些好的开发思想. 安全漏洞对于一个小项目来说,可能不是特别的重视,对于一个大项目来说,这是特别重要需要注意的,特别是在项目开发中的就要 ...
 - Winform开发框架之通用附件管理模块 --SNF快速开发平台3.3-Spring.Net.Framework
		
最近项目太多都没有时间写文章了,实际项目需求一,CS端和windows平板都需要附件上传管理功能.以前做的都是BS的附件管理和上传功能.本来计划在Winform上嵌套一个浏览器直接用bs的附件上传功能 ...
 - (转)EASYUI+MVC4通用权限管理平台
		
原文地址:http://www.cnblogs.com/hn731/archive/2013/07/15/3190947.html 通用权限案例平台在经过几年的实际项目使用,并取得了不错的用户好评.在 ...
 
随机推荐
- C# readonly修饰符
			
readonly修饰符在作祟 强化官方解释: readonly是一个修饰字段的关键字:被它修饰的字段只有在初始化或者构造函数中才能够赋值. readonly修饰的引用类型字段必须始终引用同一对象: r ...
 - Vulnhub内网渗透DC-6靶场通关
			
个人博客 xzajyjs.cn IP DC-6: 192.168.168.4 Kali: 192.168.168.5 信息搜集 arp-scan -l # nmap -sn 192.168.168.0 ...
 - docker部署fastdfs并在Django中集成
			
拉取Fastdfs镜像 docker pull delron/fastdfs 构建Tracker容器 是在Linux环境下,使用docker镜像构建tracker容器,用于启动跟踪服务器,起到调度的作 ...
 - 【LeetCode贪心#08】根据身高重建队列(还是涉及处理两个维度的信息)
			
根据身高重建队列 力扣题目链接(opens new window) 假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序).每个 people[i] = [hi ...
 - docker使用 mysql8
			
# docker pull mysql:8 # mkdir -p /mysql/{datadir,etc/mysql} # cat >/mysql/etc/mysql/my.cnf <&l ...
 - Java  类中属性的使用
			
1 类中属性的使用: 2 * 属性(成员变量) 局部变量 3 * 1.相同点: 4 * 定义变量的个格式: 数据类型 变量名 = 变量值 5 * 先声明 后使用 6 * 变量都有其对应的作用域 7 * ...
 - 3、zookeeper在java使用的API
			
引入maven包 <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient< ...
 - MySQL基础篇快速记忆和查询
			
查询 语法: SELECT 标识选择哪些列 FROM 标识从哪个表中选择 去重(Distinct) 在SELECT语句中使用关键字DISTINCT去除重复行 SELECT DISTINCT depar ...
 - vue-cli-plugin-electron-builder
			
https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/#installation 用cnpm安装 cnpm install ...
 - 基于ads1292心电数据存储和状态显示
			
一 前记 ads1292芯片在专业的心电采集这块应用十分广泛.随着医疗可穿戴的发展,便携式心电测量这块前景十分广阔.基于现在的几个项目,团队在这里花费了不少精力,借此机会把这个方向打造成团队的特色之一 ...