https://www.youtube.com/user/BoQianTheProgrammer 视频网址

Unique Lock

unique_lock和lock_guard类似,都是mutex的wrapper类,但是前者更加灵活

  • lock_guard没有unlock方法,unique_lock可以调用unlock
  • unique_lock可以延时调用lock方法,lock_guard不行
  • unique_lock不可复制、可移动,lock_guard不可复制、不可移动
// 1
...
std::unique_lock<std::mutex> locker(mu);
g_num ++;
locker.unlock();
... // 2
...
std::unique_lock<std::mutex> locker(mu, std::defer_lock);
...
locker.lock();
g_num++;
locker.unlock();
...
locker.lock();
g_num++;
locker.unlock();
... // 3
...
std::unique_lock<std::mutex> locker2 = std::move(locker);

unique_lock并不能取代lock_guard

unique_lock的灵活性是有代价的,灵活的同时其本身所占空间也越大(时间性能上不了解)

有时候并不需要unique_lock的那些灵活性,只要用lock_guard即可

Lasy Initialization

先看下面一段代码

class LogFile {
std::mutex _mu;
ofstream _f;
public:
LogFile() {
_f.open("log.txt");
}
void func(string id, int v) {
std::unique_lock<mutex> locker(_mu);
_f << id << ", " << v << endl;
}
}

打印日志的类,在构造函数里打开log.txt

但是有可能自始至终都没有打印过日志(调用func),那么_f.open就没有必要调用

可以考虑在func中调用_f.open,如下

class LogFile {
std::mutex _mu;
ofstream _f;
public:
LogFile() {
// _f.open("log.txt");
}
void func(string id, int v) {
if (!_f.is_open()) {
_f.open("log.txt");
}
std::unique_lock<mutex> locker(_mu);
_f << id << ", " << v << endl;
}
}

线程安全

这时就要考虑LogFile::func的线程安全问题(针对资源对象_f)),按照前面的方法,通过mutex来控制_f的使用

class LogFile {
std::mutex _mu_open;
ofstream _f;
public:
LogFile() { }
void func(string id, int v) {
if (!_f.is_open()) {
std::unique_lock<mutex> locker(_mu_open);
_f.open("log.txt");
}
...
}
}

上面这段代码并没有做到线程安全。场景假设:

线程A和线程B同时进到 if (_f.is_open()),这时文件并没有打开过,所以都通过了if判断,

接着A,B都会调用获取mutex,假设A先获取,然后A调用open打开文件,然后释放mutex,

接着B就会获取mutex,然后会再调用一次open。

两次打开一个文件!

不仅仅_f.open需要同步,_f.is_open也需要同步:

...
std::unique_lock<mutex> locker2(_mu_open);
if (_f.is_open()) {
_f.open("log.txt");
}
locker2.unlock();
...

但是这时又会有另外一个问题,每次func函数被调用时都会有一次mutex获取的行为,一定程度上会影响程序的并发性

std::once_flag和std::call_once

针对上面那个问题,c++提供了std::once_flag来解决:

class LogFile {
std::once_flag _flag;
ofstream _f;
public:
LogFile() { }
void func(string id, int v) {
std::call_once(_flag, [&](){_f.open("log.txt");});
...
}
}

上面代码里的lambda函数仅会被一个线程调用一次!

最新文章

  1. 2-1 git合并 打tag
  2. 亿级Web系统搭建——单机到分布式集群
  3. JDBC修改表数据
  4. rabbitmq消息队列——&quot;topic型交换器&quot;
  5. linux学习之系统管理、网络配置、软件安装
  6. WebService之Axis2 后续(6)~(10)目录
  7. Box2d引擎之元素
  8. redis 详解
  9. 条件放在left join后面和where后面
  10. Cloudera Hadoop 4 实战课程(Hadoop 2.0、集群界面化管理、电商在线查询+日志离线分析)
  11. iOS 错误 之 http请求
  12. ASP.NET Core 源码学习之 Options[4]:IOptionsMonitor
  13. 火币网现货API[Python3版]
  14. 数据处理不等式:Data Processing Inequality
  15. C# 通过 Quartz .NET 实现Timer Job并将其注册成为Windows Service
  16. [大数据面试题]hadoop核心知识点
  17. POJ1509 Glass Beads(最小表示法 后缀自动机)
  18. .NetCore 下开发独立的(RPL)含有界面的组件包 (五)授权过滤参数处理
  19. 小程序之取标签中内容 例如view,text
  20. HDFS中的集中缓存管理详解

热门文章

  1. Hibernate的批量查询——Criteria查询所有、条件、分页、统计(聚合函数)、排序
  2. presto-gateway lyft 团队开源的prestodb 的负载均衡、代理、网关工具
  3. python 脚本接受参数
  4. 实现mysql的读写分离(mysql-proxy)____2
  5. Nexus OSS私服仓库的备份与迁移
  6. 《jmeter接口自动化与性能实战-飞天小子.pdf》
  7. [Beta]Scrum Meeting#8
  8. 假如 Redis Cluster 模式用在 T-io 上
  9. ethtool 强制设置网卡运行模式为100M
  10. Dubbo Filter详解