上一節實現了智能指針,其中的拷貝構造函數和賦值運算符是經過增長/減小指針的引用計數來操做的。可是若是是管理一個獨佔資源呢?咱們但願在一個資源使用時被鎖定,在使用完畢後被釋放。 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對象的管理,採用了禁止拷貝的方式,可是對其餘對象的管理就不必定了,要根據對象的特性靈活管理。
常見的拷貝行爲有:禁止拷貝(例如本類)、引用計數(例如上節的智能指針),可是要記住,若是實現了拷貝,必定要將全部元素所有拷貝。