第五章 Unique Lock and Lazy Initialization

来源:互联网 发布:淘宝销售数据 编辑:程序博客网 时间:2024/06/05 00:09

本章学习重点:

Unique Lock

线程环境中处理初始化问题

  

  

  

前面介绍过两点"加锁"的方式:一个是使用mutex,另一个是lock_guard<std::mutex>,还有另外一种方式是使用unique_lock.

  

  

 lock_guard 最大的缺点也是简单,没有给程序员提供足够的灵活度,因此,C++11 标准中定义了另外一个与 Mutex RAII 相关类 unique_lock,该类与 lock_guard 类相似,也很方便线程对互斥量上锁,但它提供了更好的上锁和解锁控制。

  

From <http://www.cnblogs.com/haippy/p/3346477.html>

黄棒清使用的一词——"弹性",来表示unique_lock的优点。比如在案例中cout后面执行其他代码,但是该代码不需要mutex被锁住,可是使用unlocker解锁。

std::unique_lock<std::mutex> locker(m_mutex);

//std::lock_guard<mutex> locker(m_mutex);

std::cout<< "from " << id << value << endl;

locker.unlock();

//......

而unique_lock还有一个参数defer_lock,可以将该锁打开,在使用之前,直接lock一下就可以。

std::unique_lock<std::mutex> locker(m_mutex, defer_lock);

//.....

//std::lock_guard<mutex> locker(m_mutex);

locker.lock();//locker这把锁锁上下面的cout,但上面//......中的其他代码并没有上锁

std::cout<< "from " << id << value << endl;

locker.unlock();

//......

测试结果:

 

 

  

  

  

  

  

  

Note though, that the unique_lock object does not manage the lifetime of the mutex object in any way: the duration of the mutex object shall extend at least until the destruction of the unique_lock that manages it.

  

From <http://www.cplusplus.com/reference/mutex/unique_lock/>

  

  

  

std::unique_lock移动(move assign)赋值操作

std::unique_lock支持移动赋值(move assignment),但是普通的赋值被禁用了,

move (1)

unique_lock& operator= (unique_lock&& x) noexcept;

copy [deleted] (2)

unique_lock& operator= (const unique_lock&) = delete;

移动赋值(move assignment)之后,由 x所管理的 Mutex 对象及其状态将会被新的 std::unique_lock对象取代。

  

From <http://www.cnblogs.com/haippy/p/3346477.html>

  

Member types

member type

definition

description

mutex_type

The template parameter (Mutex)

The managed mutex object type

Member functions

(constructor)

Construct unique_lock (public member function )

(destructor)

Destroy unique_lock (public member function )

Locking/unlocking

lock

Lock mutex (public member function )

try_lock

Lock mutex if not locked (public member function )

try_lock_for

Try to lock mutex during time span (public member function )

try_lock_until

Try to lock mutex until time point (public member function )

unlock

Unlock mutex (public member function )

Modifiers

operator=

Move-assign unique_lock (public member function )

swap

Swap unique locks (public member function )

release

Release mutex (public member function )

Observers

owns_lock

Owns lock (public member function )

operator bool

Return whether it owns a lock (public member function )

mutex

Get mutex (public member function )

  

From <http://www.cplusplus.com/reference/mutex/unique_lock/>

  

#include <iostream> // std::cout

#include <thread> // std::thread

#include <mutex> // std::mutex, std::unique_lock

  

std::mutex mtx; // mutex for critical section

  

void print_block(int n, char c) {

// critical section (exclusive access to std::cout signaled by lifetime of lck):

std::unique_lock<std::mutex> lck(mtx);

for (int i = 0; i<n; ++i) { std::cout << c; }

std::cout << '\n';

}

  

int main()

{

std::thread th1(print_block, 5000, '*');

std::thread th2(print_block, 500, '$');

  

th1.join();

th2.join();

  

return 0;

}

  

图一:使用defer_lock,但没有lock时的结果

  

图二:使用cout之前locker这把锁锁上了cout的结果

  

Lazy Initialization

在上面的上面的例子中,f.open()在构造时就会被打开一次,而对于不需要的情况下再次的打开显然是浪费资源,因此,移动到下面的成员函数中:

  

void share_Print(std::string id, int value)

{

If(!f.is_open())

{

f.open("log.txt");

}

std::unique_lock<std::mutex> locker(m_mutex);

//std::lock_guard<mutex> locker(m_mutex);

std::cout<< "from " << id << value << endl;

}

  

但是,同cout一样,open也需要使用锁来保证线程的安全,

  

  

  

//…….

Mutex m_mutex_open;

  

void share_Print(std::string id, int value)

{

If(!f.is_open())

{

 

std::unique_lock<std::mutex> locker(m_mutex_open);

f.open("log.txt");

}

std::unique_lock<std::mutex> locker(m_mutex,std::defer_lock);

//std::lock_guard<mutex> locker(m_mutex);

std::cout<< "from " << id << value << endl;

}

  

这种情况也同样不能保证线程的安全,这是由于is_open()函数没有使用锁来保护该资源,导致多个线程同时因为抢占is_open函数时而导致死锁的发生。进一步修改上述代码

  

#include<iostream>

#include<string>

#include<thread>

#include<mutex>

#include<fstream>

  

using namespace std;

  

std::mutex mu;

class lofFile

{

public:

lofFile()

{

//f.open("log.txt");//构造函数中打开log.txt文件

}

void share_Print(std::string id, int value)

{

{

std::unique_lock<std::mutex> locker(m_mutex1);

if (!f.is_open())

{

f.open("log.txt");

}

}

  

std::unique_lock<std::mutex> locker(m_mutex);

  

//std::lock_guard<mutex> locker(m_mutex);

std::cout<< "from " << id << value << endl;

}

  

protected:

private:

mutex m_mutex;

mutex m_mutex1;

ofstream f; //m_mutex保护的对象

};

  

void function_1(lofFile& l)

{

for (int i = 0; i > -100; i--)

{

//std::cout << "From t1:" << i << std::endl;

//using shared_Print function instead of function_1

l.share_Print("From t1", i);

  

}

}

int main()

{

lofFile l;

thread t1(function_1, std::ref(l));

for (int i = 0; i < 100; i++)

{

//cout << "From main:" << i << endl;

l.share_Print("From Main()", i);

}

t1.join();

}

  

  

为了简洁起见,C++11委员会人员提供一种解决方案。使用lazy initialization

将上面的

mutex m_mutex1;

修改为std::once_flag m_flag;将锁和文件使用lambda来代替(能够保证一个线程只能被调用一次):

  

std::call_once(m_flag, [&]() {f.open("log.txt"); });

  

清单:使用lazy initializatin

#include<iostream>

#include<string>

#include<thread>

#include<mutex>

#include<fstream>

  

using namespace std;

  

std::mutex mu;

class lofFile

{

public:

lofFile()

{

//f.open("log.txt");

}

void share_Print(std::string id, int value)

{

//{

//std::unique_lock<std::mutex> locker(m_mutex1, defer_lock);

//if (!f.is_open())

//{

//f.open("log.txt");

//}

//}

//直接使用m_flag

std::call_once(m_flag, [&]() {f.open("log.txt"); });

  

std::unique_lock<std::mutex> locker(m_mutex, defer_lock);

//.....

//std::lock_guard<mutex> locker(m_mutex);

locker.lock();//locker这把锁锁上下面的cout,但上面//......中的其他代码并没有上锁

std::cout<< "from " << id << value << endl;

locker.unlock();

//......

}

  

protected:

private:

mutex m_mutex;

mutex m_mutex1;

std::once_flag m_flag;

ofstream f; //m_mutex保护的对象

};

  

void function_1(lofFile& l)

{

for (int i = 0; i > -100; i--)

{

//std::cout << "From t1:" << i << std::endl;

//using shared_Print function instead of function_1

l.share_Print("From t1", i);

  

}

}

int main()

{

lofFile l;

thread t1(function_1, std::ref(l));

for (int i = 0; i < 100; i++)

{

//cout << "From main:" << i << endl;

l.share_Print("From Main()", i);

}

t1.join();

}

  

  

  

测试结果:

  

  

  

  

  

  

  

  

  

  

  

  

  

  

  

  

  

  

0 0
原创粉丝点击