當建構函式洩露this指標時
當一個類正在構造時在建構函式中將this洩露給了其它物件,這在單執行緒序列執行情況下可能沒有什麼問題,但是在多執行緒下那麼問題就比較大了。比如執行緒1負責構造這個物件A但是在建構函式中將this指標洩露給了其它執行緒所呼叫的物件B,不巧的是其它執行緒所呼叫的物件B看見A有些不爽將其析構了。那麼最後A自以為一切構造好了返回,執行緒1然後對這個A操作,最後可怕的錯誤(比如段錯誤)無窮無盡的折磨執行緒1......
模擬這個問題:假設有一個人A,餐館B,A沒錢就去餐館B點菜吃飯,單執行緒模式下:A進入餐館點菜->餐館B炒菜(不辭辛勞)->A吃完了沒錢付->餐館B大為光火.....此後出現了一幕CATV不會報道的事件.......
但是聰明的餐館B現在學聰明瞭,也會利用高科技了,利用某種技術可以檢測到使用者是否有錢(比如和銀行通姦哈哈)從而開啟了個後臺程序檢測客戶沒錢立馬轟走.....那麼問題變成如下:
執行緒1:客人A進入餐館B點菜,點菜這個操作這裡強加為:A將this指標洩露給了B
執行緒2:餐館B發現有客人來了->獲取該客人的資訊(這裡是this指標)->通過科學技術發現客戶沒錢->不準廚房炒菜了,立馬暴打客戶一頓
在這種模式下顯然客戶不可能吃完飯再捱揍了.....這就是洩露this指標的可怕之處,我還沒有構造好,其它執行緒就把我給幹掉了....我以後還咋活啊???
輸出結果:#include<iostream> #include<unistd.h> #include<pthread.h> #include<assert.h> using namespace std; class Hotel;//餐館 class Person{//人 public: Person(int a,Hotel* h);//a是代表人身上多少錢,h是吃飯的餐館 int get(){ return *money; } ~Person(); private: int* money; }; class Hotel{//餐館 public: Hotel():flag(false),p(NULL){} void check(){//餐館檢查客人是不是有錢 if(flag&&p->get()<=0){//flag用於是否有客人,p是客人若客人的錢get()小於等於0表示沒錢要吃霸王餐啊,果斷轟出去 p->~Person();//幹掉這個吃霸王餐的人 } } void _register(Person* a){//註冊是否有客人來,這個函式交給Person類即客人來了就告訴餐館我來了 flag=true; p=a; } private: bool flag; Person* p; }; Hotel* h=new Hotel; Person::Person(int a,Hotel* h){ money=new int(a); h->_register(this);//客人向餐館註冊 sleep(3);//休眠的原因是讓建構函式在此停止,造成正在構造的情形(正在等菜....),那麼餐館的執行緒就有時間檢查客人是不是有錢 } Person::~Person(){ delete money; } void* worker1(void* arg){//客人執行緒 Person* temp=(Person*)arg; temp=new Person(-10,h);//客人temp欠債10塊(這裡-10的money可以理解為一張催款單)還進了餐館吃飯 cout<<temp->get()<<endl;//客人看下催款單還在不在...我還要去繳費呢... } void* worker2(void* arg){//餐館工作執行緒 Person* p=(Person*)arg;// sleep(1);//睡眠1s是為了讓客人進入建構函式但是又沒有離開建構函式 h->check();//在客人還沒有構造完成時(比如正在等菜時...)立刻檢查客人是否有錢,很不幸的是餐館發現客人的催款單一怒把催款單銷燬delete了,並欲把客人轟出去 } int main(){ pthread_t pthd[2]; Person* one;//客人 int ret=pthread_create(&pthd[0],NULL,worker1,one);//客人執行緒 assert(ret==0); ret=pthread_create(&pthd[1],NULL,worker2,h);//餐館執行緒 assert(ret==0); ret=pthread_join(pthd[0],NULL); assert(ret==0); ret=pthread_join(pthd[1],NULL); assert(ret==0); return 0; }
0 //本來客戶預期是點完菜還要做其他事呢,可是餐館發現他沒錢立馬暴打他並轟走他....想吃飽飯再捱打不可能了....
總結:
物件構造實現執行緒安全原則是:不要在構造期間洩露this指標即:不要在建構函式中註冊回撥函式,不要在建構函式中把this指標傳給跨執行緒的物件,即使在建構函式最後一行也不行(比如基類A,子類B先構造基類A,A在構造最後一行向其它物件C註冊了什麼而B正在構造...,C拿著A的虛擬函式執行什麼關於B的操作....發生了錯誤)。 不然讓別的執行緒看到這個半成品物件會幹出什麼事誰也不知道(比如廢掉物件)....那麼那些註冊回撥函式的操作怎麼辦?不寫在建構函式中,還可以寫在其它成員函式中嘛,只不過使用起來多了次成員函式呼叫罷了。為了安全,你懂的....