一 前提,lock_guard 详解:
lock_guard是 个类模版,不同的是 lock_guard 的这个模版只能<mutex>,
lock_guard<mutex> mylock_guard(mymutex);
可以看到,lock_guard在实例化对象的时候,有两种构造方法。
我们这里看有两个参数的构造方法,也就是第二个参数 是std::adopt_lock_t的构造方法。
std::adopt_lock_t |
假设调用方线程已拥有互斥的所有权 |
struct adopt_lock_t { explicit adopt_lock_t() = default; }; |
从C++的文档中可以看出,这玩意是个 struct,实际上,理解为标志位更为合适。
adopt_lock_t 意思是:我们在lock_guard构造函数中不调用 mutex1.lock(),只是在析构函数中调用 mutex1.unlock();
mymutex1.lock()
lock_guard<mutex> sbguard1(mymutex1,adopt_lock);
二 unique_lock 详解
unique_lock 也是 类模版,只能使用<mutex>,
unique_lock的构造函数比lock_guard的构造函数多了几个:当前我们研究一下第二个参数是 ;
adopt_lock 意思是:调用这一行之前,一定是有lock()过的,我们在lock_guard构造函数中不调用 mutex1.lock(),只是在析构函数中调用 mutex1.unlock();
mymutex1.lock()
unique_lock <mutex> sbguard1(mymutex1,adopt_lock);
try_to_lock 意思是:我们会尝试用mutex的lock()去锁定这个mutex,但是如果没有锁定成功,我也会立即返回,并不会阻塞在这里。那么怎么知道锁定成功了吗?
owns_lock()方法说明:检查 *this 是否占有锁定的互斥。
例子:
mymutex1.lock()
unique_lock <mutex> sbguard1(mymutex1,try_to_lock);
if(sbguard1.owns_lock()){
//如果拿到了锁子的逻辑
} else{
//没拿到锁子的逻辑
}
defer_lock 意思是:并没有给mutex加锁:初始化一个没有加锁的mutex,那么不能给mutex加锁,这个有啥用?
-------实际上要结合 unique_lock 的成员函数来能理解为什么要这么干。
lock()函数
好处是不用管unlock(),在 sbguard1的析构函数调用的时候,会调用 mymutex1的析构函数
unique_lock<mutex> sbguard1(mymutex1, defer_lock);
unique_lock<mutex> sbguard2(mymutex2, defer_lock);
sbguard1.lock();
sbguard2.lock();
class Teacher171 {
public:
//共享数据 存在list中
list<int> msgListQueue;
list<int> msgListQueue2;
mutex mymutex1;
mutex mymutex2;
int readcount = 0;//记录已经读取了多少个。
public:
//读取 共享数据的线程方法
void readfunc() {
while (true) {
//只要不为空,就可以读取数据
if (readcount >= 2000) {
break;
}
//fix方案3.使用lock(mutex1,mutex2); 参数都是mutex,代表的就是顺序,因此保证每个地方的调用都是一样的就可以了
// 这里使用 lock_guard<mutex> sbguard1(mymutex1,adopt_lock);的含义是:在lock_guard构造函数中不调用 mutex1.lock(),只是在析构函数中调用 mutex1.unlock();
lock(mymutex1, mymutex2);
lock_guard<mutex> sbguard1(mymutex1, adopt_lock);
lock_guard<mutex> sbguard2(mymutex2, adopt_lock);
if (!msgListQueue.empty()) {
int readvalue = msgListQueue.front();//每次都读取第一个
cout << "读取到的值为" << readvalue << " readcount = " << readcount << endl;
msgListQueue.pop_front();//删除第一个元素
readcount++;
}
else {
cout << "没有读取到值" << endl;
}
if (!msgListQueue2.empty()) {
int readvalue = msgListQueue2.front();//每次都读取第一个
cout << "222读取到的值为" << readvalue << " readcount = " << readcount << endl;
msgListQueue2.pop_front();//删除第一个元素
readcount++;
}
else {
cout << "222没有读取到值" << endl;
}
}
}
//写入 共享数据的线程方法
void writefunc() {
for (size_t i = 0; i < 1000; i++)
{
unique_lock<mutex> sbguard1(mymutex1, defer_lock);
unique_lock<mutex> sbguard2(mymutex2, defer_lock);
sbguard1.lock();
sbguard2.lock();
msgListQueue.push_back(i);//每次都写到末尾
cout << "写入元素的值为" << i << endl;
}
}
public:
Teacher171() {
cout << "Teacher171 构造方法 this = " << this << endl;
}
Teacher171(const Teacher171 & obj) {
cout << "Teacher171 copy 构造方法 this = " << this << " obj = " << &obj << endl;
}
~Teacher171() {
cout << "Teacher171 析构函数 this = " << this << endl;
}
};
void main() {
cout << "=========================" << endl;
Teacher171 tea1;
thread readthread1(&Teacher171::readfunc, &tea1);
thread writethread1(&Teacher171::writefunc, &tea1);
thread readthread2(&Teacher171::readfunc, &tea1);
thread writethread2(&Teacher171::writefunc, &tea1);
readthread1.join();
writethread1.join();
readthread2.join();
writethread2.join();
}
unlock()函数
之前的lock_guard是要在析构函数调用的时候,才会调用 mutex.unlock();
这有时候不方便,因此提供了unlock()方法
class Teacher171 {
public:
//共享数据 存在list中
list<int> msgListQueue;
list<int> msgListQueue2;
mutex mymutex1;
mutex mymutex2;
int readcount = 0;//记录已经读取了多少个。
public:
//读取 共享数据的线程方法
void readfunc() {
while (true) {
//只要不为空,就可以读取数据
if (readcount >= 2000) {
break;
}
//fix方案3.使用lock(mutex1,mutex2); 参数都是mutex,代表的就是顺序,因此保证每个地方的调用都是一样的就可以了
// 这里使用 lock_guard<mutex> sbguard1(mymutex1,adopt_lock);的含义是:在lock_guard构造函数中不调用 mutex1.lock(),只是在析构函数中调用 mutex1.unlock();
lock(mymutex1, mymutex2);
lock_guard<mutex> sbguard1(mymutex1, adopt_lock);
lock_guard<mutex> sbguard2(mymutex2, adopt_lock);
if (!msgListQueue.empty()) {
int readvalue = msgListQueue.front();//每次都读取第一个
cout << "读取到的值为" << readvalue << " readcount = " << readcount << endl;
msgListQueue.pop_front();//删除第一个元素
readcount++;
}
else {
cout << "没有读取到值" << endl;
}
if (!msgListQueue2.empty()) {
int readvalue = msgListQueue2.front();//每次都读取第一个
cout << "222读取到的值为" << readvalue << " readcount = " << readcount << endl;
msgListQueue2.pop_front();//删除第一个元素
readcount++;
}
else {
cout << "222没有读取到值" << endl;
}
}
}
//写入 共享数据的线程方法
void writefunc() {
for (size_t i = 0; i < 1000; i++)
{
unique_lock<mutex> sbguard1(mymutex1, defer_lock);
unique_lock<mutex> sbguard2(mymutex2, defer_lock);
sbguard1.lock();
sbguard2.lock();
msgListQueue.push_back(i);//每次都写到末尾
cout << "写入元素的值为" << i << endl;
sbguard1.unlock();
sbguard2.unlock();
}
}
public:
Teacher171() {
cout << "Teacher171 构造方法 this = " << this << endl;
}
Teacher171(const Teacher171 & obj) {
cout << "Teacher171 copy 构造方法 this = " << this << " obj = " << &obj << endl;
}
~Teacher171() {
cout << "Teacher171 析构函数 this = " << this << endl;
}
};
void main() {
cout << "=========================" << endl;
Teacher171 tea1;
thread readthread1(&Teacher171::readfunc, &tea1);
thread writethread1(&Teacher171::writefunc, &tea1);
thread readthread2(&Teacher171::readfunc, &tea1);
thread writethread2(&Teacher171::writefunc, &tea1);
readthread1.join();
writethread1.join();
readthread2.join();
writethread2.join();
}
try_lock()函数
我们会尝试用mutex的lock()去锁定这个mutex,但是如果没有锁定成功,我也会立即返回,并不会阻塞在这里。那么怎么知道锁定成功了吗?mymutex1.lock()
unique_lock <mutex> sbguard1(mymutex1,adopt_lock);
if(sbguard1.owns_lock()){
//如果拿到了锁子的逻辑
} else{
//没拿到锁子的逻辑
}
class Teacher171 {
public:
//共享数据 存在list中
list<int> msgListQueue;
mutex mymutex1;
public:
//读取 共享数据的线程方法
void readfunc() {
for (size_t i = 0; i < 10000; ++i) {
unique_lock<mutex> sbguard1(mymutex1,defer_lock);
if (sbguard1.try_lock() == true) {
if (!msgListQueue.empty()) {
int readvalue = msgListQueue.front();//每次都读取第一个
cout << "读到的值为" << readvalue << endl;
msgListQueue.pop_front();//删除第一个元素
}
}
else {
cout << "读取第" << i << "次" << endl;
}
}
}
//写入 共享数据的线程方法
void writefunc() {
for (size_t i = 0; i < 10000; ++i)
{
unique_lock<mutex> sbguard1(mymutex1, defer_lock);
if (sbguard1.try_lock() == true) {
msgListQueue.push_back(i);//每次都写到末尾
cout << "写入的值为" << i << endl;
}
else {
cout << "写入第" << i << "次" << endl;
}
}
}
public:
Teacher171() {
cout << "Teacher171 构造方法 this = " << this << endl;
}
Teacher171(const Teacher171 & obj) {
cout << "Teacher171 copy 构造方法 this = " << this << " obj = " << &obj << endl;
}
~Teacher171() {
cout << "Teacher171 析构函数 this = " << this << endl;
}
};
void main() {
cout << "=========================" << endl;
Teacher171 tea1;
thread readthread1(&Teacher171::readfunc, &tea1);
thread writethread1(&Teacher171::writefunc, &tea1);
readthread1.join();
writethread1.join();
}
release()函数
返回它所管理的mutex对象指针,并释放所有权,也就是说:这个uique_lock 和mutex不在有关系,如果这时候mutex处理加锁状态,则要程序员自己对这个mutex进行unlock操作。
unique_lock<mutex> sbguard1(mymutex1,defer_lock);
if (sbguard1.try_lock() == true) {
if (!msgListQueue.empty()) {
int readvalue = msgListQueue.front();//每次都读取第一个
cout << "读到的值为" << readvalue << endl;
msgListQueue.pop_front();//删除第一个元素
}
mutex* ponitmutex = sbguard1.release();//这时候加上锁子了,我们将sbguard1和它内部的mutex关系解除了,那么下面就要自己对 mutex进行unlock
ponitmutex->unlock();
}
unique_lock所有权转移
unique_lock<mutex> sbguard1(mymutex1, defer_lock);
unique_lock<mutex> sbguard2(move(sbguard1));
C++文档的解释:
//unique_lock( unique_lock&& other ) noexcept; 2) 移动构造函数。以 other 的内容初始化 unique_lock 。令 other 无关联互斥。
意思:
1.sbguard1 和 mymutex1 不在有关系
2.sbguard2 和 mymutex1 关联