#include <iostream>
#include <fstream>
#include <thread>
#include <mutex>
#include <string>
using namespace std;
class LogFile {
public:
LogFile() {
f.open("log.txt");
}
~LogFile() {
}
void shared_print(string msg, int id) {
lock_guard<mutex> guard(mu);
f<<msg<<id<<endl;
}
// Never return f to the outside world
ofstream& getStream() { return f;}
// Never pass f as an augument to user provided function
void processf(void fun(ofstream&)) {
fun(f);
}
private:
ofstream f;
mutex mu;
};
void function_1(LogFile& log) {
for(int i = 0; i >-100; i--) {
log.shared_print("From t1: ",i);
}
}
int main()
{
LogFile log;
thread t1(function_1,ref(log));
for(int i= 0; i < 100; i++) {
log.shared_print("From main: ",i);
}
t1.join();
return 0;
}
When multiple locks need to be applied at the same time, the following two methods are used
lock(mtx1,mtx2)
lock_guard(mtx1,adopt_lock)
lock_guard(mtx2,adopt_lock)
unique_lock lock1(mtx1,defer_lock)
unique_lock lock2(mtx2,defer_lock)
lock(lock1,lock2)
unique_lock VS lock_guard
lock_guard doesn't allow manual unlock/lock less performance intensive
unique_lock is more flexible, allows manual unlock/lock multiple times, high performance consumption
void foo() {
unique_lock<mutex> locker(mtx,defer_lock); defer_lock assumes no locking yet
// do something not using mtx
mtx.lock();
// do something using mtx to protect
mtx.unlock();
// do something else
}
Lazy Initialization
#include <iostream>
#include <thread>
#include <mutex>
#include <fstream>
#include <string>
using namespace std;
class LogFile {
private:
ofstream f;
mutex _mu;
mutex _mu_open;
public:
void shared_print(string& msg, int id) {
{
unique_lock<mutex> open_lck(_mu_open);
if(!f.is_open()) {
f.open("log.txt");
}
}
unique_lock<mutex> locker(_mu);
// do other things
}
};
class LazyInitializationLogFile {
private:
ofstream f;
mutex _mu;
once_flag _flag;
public:
void shared_print(string7 msg, int id) {
call_once(_flag,[&](){f.open("log.txt");});
unique_lock<mutex> locker(_mu);
// do other things
}
}
int main()
{
return 0;
}