上一节实现了智能指针,其中的拷贝构造函数和赋值运算符是经过增长/减小指针的引用计数来操做的。可是若是是管理一个独占资源呢?咱们但愿在一个资源使用时被锁定,在使用完毕后被释放。 ios
#include <mutex> c++
#include <thread> 程序员
#include <iostream> 函数
using namespace std; spa
mutex mu; 线程
int rc=5; 设计
void thread1(){ 指针
//mu.lock(); c++11
rc+=5; 对象
cout<<"thread1:"<<rc<<endl;
//mu.unlock();
}
void thread2(){
//mu.lock();
rc-=5;
cout<<"thread2:"<<rc<<endl;
//mu.unlock();
}
int main(){
thread th1(thread1);
thread th2(thread2);
th1.join();
th2.join();
}
在这里,我先把互斥代码去掉,编译运行后的结果是:
C:\Users\SkyFire\Desktop>g++ temp.cpp -std=c++11
C:\Users\SkyFire\Desktop>a
thread1:thread2:510
C:\Users\SkyFire\Desktop>a
thread1:thread2:105
C:\Users\SkyFire\Desktop>a
thread1:thread2:105
C:\Users\SkyFire\Desktop>a
thread1:thread2:510
每次的结果都不肯定,由于没加互斥。
那么,把互斥加上:
#include <mutex>
#include <thread>
#include <iostream>
using namespace std;
mutex mu;
int rc=5;
void thread1(){
mu.lock();
rc+=5;
cout<<"thread1:"<<rc<<endl;
mu.unlock();
}
void thread2(){
mu.lock();
rc-=5;
cout<<"thread2:"<<rc<<endl;
mu.unlock();
}
int main(){
thread th1(thread1);
thread th2(thread2);
th1.join();
th2.join();
}
编译运行的结果是:
C:\Users\SkyFire\Desktop>g++ temp.cpp -std=c++11
C:\Users\SkyFire\Desktop>a
thread1:10
thread2:5
C:\Users\SkyFire\Desktop>a
thread1:10
thread2:5
C:\Users\SkyFire\Desktop>a
thread1:10
thread2:5
可是某些时候,咱们可能会将unlock的动做漏写(百密一疏),以下面这种:
#include <mutex>
#include <thread>
#include <iostream>
using namespace std;
mutex mu;
int rc=5;
void thread1(){
mu.lock();
rc+=5;
cout<<"thread1:"<<rc<<endl;
//mu.unlock();
}
void thread2(){
mu.lock();
rc-=5;
cout<<"thread2:"<<rc<<endl;
mu.unlock();
}
int main(){
thread th1(thread1);
thread th2(thread2);
th1.join();
th2.join();
}
这样的结果就是thread2里面的语句一直得不到执行,程序死锁。
编译运行:
C:\Users\SkyFire\Desktop>g++ temp.cpp -std=c++11
C:\Users\SkyFire\Desktop>a
thread1:10
^C
C:\Users\SkyFire\Desktop>
能够看到,thread2一直没有执行,后面的^C是我使用Ctrl+C中断的结果。
为了不这种状况,咱们使用资源管理类。
一个简单的实现:
class AutoMutex{
private:
mutex μ
public:
AutoMutex(mutex &t):mu(t){
mu.lock();
}
~AutoMutex(){
mu.unlock();
}
};
这个类在构造的时候会将一个互斥量锁定,而在析构时会释放掉这个互斥量。乍一看好像没什么问题。事实上,在"正常的"状况下,这段代码能够工做的很好。
mutex mu;
void mythread(){
AutoMutex t(mu);
cout<<"hello world"<<endl;
}
int main(){
for(int i=0;i<10;++i)
thread(mythread).detach();
system("pause");
}
输出:
C:\Users\SkyFire\Desktop>g++ temp.cpp -std=c++11
C:\Users\SkyFire\Desktop>a
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
请按任意键继续. . .
可是,若是出现一些比较调皮的程序员(暂定为小明吧)。
调皮的小明写出了以下的代码:
mutex mu;
mutex mu2;
void mythread(){
AutoMutex t(mu);
AutoMutex t2(mu2);
t2=t;
cout<<"hello world"<<endl;
}
int main(){
for(int i=0;i<10;++i)
thread(mythread).detach();
system("pause");
}
这TM就尴尬了……小明将管理了两个不一样的mutex的对象相互赋值了。不过还好,这段代码是编译不经过的(小明的奸计未能得逞)。由于mutex类是不容许复制的,他的赋值运算符是删除的。(假设mutex能够复制,会产生什么?)
并且,管理两个mutex的对象的赋值没有任何意义,这个对象就是建立与销毁,并无其余任何做用,因此,对于这个类,只要简单地把拷贝构造函数和赋值运算符屏蔽就行了:
class AutoMutex{
private:
const AutoMutex& operator=(const AutoMutex&)=delete;
AutoMutex(const AutoMutex&)=delete;
mutex μ
public:
AutoMutex(mutex &t):mu(t){
mu.lock();
}
~AutoMutex(){
mu.unlock();
}
};
为了应对本宝宝的机智,小明又写出下面这段代码:
mutex mu;
void mythread(){
AutoMutex t(mu);
AutoMutex t2(mu);
cout<<"hello world"<<endl;
}
int main(){
for(int i=0;i<10;++i)
thread(mythread).detach();
system("pause");
}
不得不说,小明是很奸诈的~~~
一个互斥锁,对于一个线程来讲,只有获取和没获取两种状态,而不存在获取两次这种状态。而不存在什么获取屡次什么的状态。
咱们先看一下,对于mutex,获取屡次是个什么结果:
mutex mu;
void mythread(){
mu.lock();
mu.lock();
cout<<"hello world"<<endl;
mu.unlock();
mu.unlock();
}
int main(){
for(int i=0;i<10;++i)
thread(mythread).detach();
system("pause");
}
运行结果:
C:\Users\SkyFire\Desktop>g++ temp.cpp -std=c++11
C:\Users\SkyFire\Desktop>a
请按任意键继续. . .
既然mutex自己就是这么设计的,咱们仍是不改的好~~~
猜测mutex这样设计是为了提供PV锁机制:
下面这段代码,不加任何互斥:
int main(){
cout<<1<<endl;
thread([](){cout<<3<<endl;}).detach();
cout<<2<<endl;
thread([](){cout<<4<<endl;}).detach();
cout<<5<<endl;
}
输出结果为:
C:\Users\SkyFire\Desktop>g++ temp.cpp -std=c++11
C:\Users\SkyFire\Desktop>a
1
3
2
54
彻底没有顺序可言,可是若是加上一些互斥。
mutex mu;
int main(){
cout<<1<<endl;
thread([](){cout<<3<<endl;mu.unlock();}).detach();
mu.lock();
cout<<2<<endl;
mu.lock();
thread([](){cout<<4<<endl;mu.unlock();}).detach();
mu.lock();
cout<<5<<endl;
mu.unlock();
}
此时的输出结果为:
C:\Users\SkyFire\Desktop>g++ temp.cpp -std=c++11
C:\Users\SkyFire\Desktop>a
1
2
3
4
5
Perfect!!!
这正是mutex为咱们提供的特性,既然咱们是管理mutex,咱们就不应破坏这种特性。
因而~~~上面全是小明的错^_^。
这里实现的只是对mutex对象的管理,采用了禁止拷贝的方式,可是对其余对象的管理就不必定了,要根据对象的特性灵活管理。
常见的拷贝行为有:禁止拷贝(例如本类)、引用计数(例如上节的智能指针),可是要记住,若是实现了拷贝,必定要将全部元素所有拷贝。