c ++ 11 multithreaded record 5: Unique Lock and delayed initialization

https://www.youtube.com/user/BoQianTheProgrammer video URL

Unique Lock

unique_lock and lock_guard similar, they are mutex of wrapper classes, but the former is more flexible

  • lock_guard not unlock method, unique_lock can call unlock
  • unique_lock can call the delay lock method, lock_guard not
  • unique_lock not be copied, can be moved, not copied lock_guard nonremovable
// 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 not substituted lock_guard

unique_lock flexibility comes at a cost, flexible space while their own share of the larger (do not understand on-time performance)
and sometimes do not need those unique_lock of flexibility, as long as you can lock_guard

Lasy Initialization

Look at the following piece of code

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;
    }
}

Class print log, open in the constructor log.txt
but there may never had the printed journal (call func), then there is no need to call _f.open
consider calling _f.open in func as follows

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;
    }
}

Thread Safety

Then you must consider the security thread of LogFile :: func (for resource objects _f)), in accordance with the previous method, to control the use _f by mutex

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");
        }
        ...
    }
}

The code above do not thread-safe. Scenario assumes:
thread A and thread B to simultaneously if (_f.is_open()), then the file is not opened, so if the judgment through,
then A, B will call acquires the mutex, suppose A first acquired, then A call to open the file open, and then released mutex,
then B will get mutex, and then will be called once again open.
Twice to open a file!
So not only _f.open need to be synchronized, _f.is_open also need to be synchronized, it is easy to think:

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

But at this time and there will be another problem, func each time the function is called, there will be a mutex acquired behavior will affect the concurrency of the program to some extent

std::once_flag和std::call_once

For that question above, c ++ provides std :: once_flag to solve:

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

The above code of lambda function is a thread called only once!

Guess you like

Origin www.cnblogs.com/ChenLambda/p/11739307.html