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函数仅会被一个线程调用一次!

c++11多线程记录5: Unique Lock和延时初始化的更多相关文章

  1. c++11多线程记录0

    两种并发编程模型 多进程 进程间通信常用的几种方式: 文件 管道 消息队列 多线程 一个进程中存在的多个线程,通常通过共享内存来通信,(说的非常非常粗俗,就是通过类似"全局变量"的 ...

  2. c++11多线程记录6:条件变量(condition variables)

    https://www.youtube.com/watch?v=13dFggo4t_I视频地址 实例1 考虑这样一个场景:存在一个全局队列deque,线程A向deque中推入数据(写),线程B从deq ...

  3. c++11多线程记录4:死锁

    简单示例 举个例子,桌上有一支笔和一张纸,小A和小B都要拿到纸笔写字 小A拿了笔,小B拿了纸,这时就形成了死锁(两人都不愿意让出纸笔). 其实只要稍加控制就可以避免这种情况:规定必须先拿到纸再能去尝试 ...

  4. c++11多线程记录3: 数据争用和Mutex的使用

    https://www.youtube.com/watch?v=3ZxZPeXPaM4 学习视频 数据争用 简单来说就是存在多个线程同时对某个共同的对象进行读写(至少有一个线程在做写操作),造成读取这 ...

  5. c++11多线程记录2:线程管理

    线程没有调用join和detach thread对象必须调用join或者detach,否则程序会终止 例如: void func() { std::cout << "hello, ...

  6. c++11多线程记录1 -- std::thread

    启动一个线程 话不多说,直接上代码 void func(); int main() { std::thread t(func); //这里就开始启动线程了 t.join(); // 必须调用join或 ...

  7. c++11 多线程 -- 基本使用

    c++11 多线程 – 基本使用 前言:这篇文章仅针对没有使用过c++11线程库的童鞋来高速入门,也是自己的一个简单记录,内容比較基础. 1.线程的基本使用 2.相互排斥量 3.条件变量 4.原子变量 ...

  8. 【转】C++ 11 并发指南一(C++ 11 多线程初探)

    引言 C++ 11自2011年发布以来已经快两年了,之前一直没怎么关注,直到最近几个月才看了一些C++ 11的新特性,算是记录一下自己学到的东西吧,和大家共勉. 相信Linux程序员都用过Pthrea ...

  9. C++11 并发指南一(C++11 多线程初探)

    引言 C++11 自2011年发布以来已经快两年了,之前一直没怎么关注,直到最近几个月才看了一些 C++11 的新特性,今后几篇博客我都会写一些关于 C++11 的特性,算是记录一下自己学到的东西吧, ...

随机推荐

  1. ava js 测试框架基本试用

    随着js 越来越强大,日常使用中关于js 的问题也就越突出了,我们需要关注的点也就不能只像以前那样 只编写简单的功能实现,我们同时也需要关注js 的健壮性,测试就是其中一个比较重要的环节,以下 是av ...

  2. A1016 | 磨人的大模拟

    这题写得头晕……明天我再评价 #include <stdio.h> #include <memory.h> #include <math.h> #include & ...

  3. 使用Visual Studio Code编辑Processing

    最近想弄Sublime Text 3写Processing,但由于各种不知名原因导致无法编译,就想着换自去年以来超火的VScode试一下,还真给我试成功了. 1.下载https://code.visu ...

  4. centos8.0安装docker

    背景简介: 前两天红帽正式发布了RHEL8,网上同时也有了CentOS8,一直在接触容器方面,为了尝鲜,下载了CentOS8,并尝试安装docker,不料竟然还报了个错(缺少依赖),故及时记录一下,方 ...

  5. 模拟30A 题解

    A. 树 联想起远古考试时做的题 记忆的轮廓. 树上走一些步数的期望. 显然可以直接解方程. 然而复杂度$O(qn^3)$,利用树上的性质优化一下, 直接一遍dfs过程中解出来,可以$O(qnlogm ...

  6. Cesium中的几种坐标和相互转换【转】

    几个重要的坐标对象:1.世界坐标 Cartesian3:笛卡尔空间直角坐标系 new Cesium.Cartesian3(x, y, z) 可以看作,以椭球中心为原点的空间直角坐标系中的一个点的坐标. ...

  7. 【深入学习linux】git的使用

    git的安装 官网下载地址:https://git-scm.com/downloads 安装完成后,还需要最后一步设置,在命令行输入: $ git config --global user.name ...

  8. Linux权限:提示-bash: ./startup.sh: Permission denied的解决方案

    Linux权限:提示-bash: ./startup.sh: Permission denied的解决方案 Linux上启动Tomcat,结果弹出:-bash: ./startup.sh: Permi ...

  9. 【Python】解析Python中的迭代器

    目录结构: contents structure [-] Iterator VS Iterable Itertools 模块 生成器(Generator) 在开始文章之前,先贴上一张Iterable. ...

  10. Springboot 条件注解

    @Conditional 根据满足某一个特定条件创建一个特定的 Bean.就是根据特定条件来控制 Bean 的创建行为,这样我们可以利用这个特性进行一些自动的配置 Springboot 中大量用到了条 ...