码迷,mamicode.com
首页 > 编程语言 > 详细

c++11多线程记录5: Unique Lock和延时初始化

时间:2019-10-25 18:03:06      阅读:98      评论:0      收藏:0      [点我收藏+]

标签:wrap   mutex   lambda函数   UNC   void   判断   int   unique   call   

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和延时初始化

标签:wrap   mutex   lambda函数   UNC   void   判断   int   unique   call   

原文地址:https://www.cnblogs.com/ChenLambda/p/11739307.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!