1. 程式人生 > >C++併發實戰3:向thread傳遞引數

C++併發實戰3:向thread傳遞引數

         在建立thread object時可以向執行緒傳遞引數,預設情況下,引數會被拷貝到執行緒空間以供執行緒執行時存取,即使引數是引用也是這樣。

情形1:

void f(int i,std::string const& s);
        boost::thread t(f,3,”hello”);            //字串常量“hello”會被轉換為string,該string線上程中存在。

情形2:
void f(int i,std::string const& s);
void oops(int some_param)
{
 char buffer[1024]; 
 sprintf(buffer, "%i",some_param);
 boost::thread t(f,3,buffer); //換成boost::thread t(f,3,string(buffer))保證安全
 t.detach();
}

這裡會存在一個潛在的風險:當oops退出時,buffer會被銷燬,但是如果此時在子執行緒中buffer還沒有被轉化成string,將會出現dangle pointer。一個解決辦法是:boost::thread t(f,3,string(buffer));此時thread在構造時就會將buffer轉換為string,並將string拷貝至子執行緒地址空間。

情形3:thread構造會拷貝其引數

#include<iostream>
#include<string>
#include<boost/thread.hpp>
#include<unistd.h>
using namespace std;
class test{
    public:
        test(int i=0):data(i){}
        test(const test& one){
            data=one.data;
            cout<<"test::copy constructor"<<endl;
        }
        test& operator=(const test& one){
            data=one.data;
            cout<<"test::operator=()"<<endl;
            return *this;
        }
        ~test(){
            cout<<"test::destructor"<<endl;
        }
    public:
        int data;
};
void func(test& one){
    cout<<"func get the data "<<one.data<<endl;
}
void oops(){
    test one(10);
    boost::thread t(func,one);
    t.join();
}
int main(){
    oops();
    return 0;
}


程式輸出:

test::copy constructor
test::copy constructor
test::copy constructor
test::copy constructor
test::copy constructor
test::destructor
test::copy constructor
test::destructor
test::destructor
test::copy constructor
test::copy constructor
test::destructor
test::destructor
test::destructor
test::destructor
func get the data 10                  //這裡是執行緒函式的輸出
test::destructor
test::destructor
   說明:即使執行緒函式func採用引用方式,thread並不知道這一情形,所以在thread object在構造時會盲目的拷貝test one,然後thread將這個物件拷貝到執行緒地址空間作為internal copy,此後執行緒函式引用的是執行緒地址空間的那個internal copy,執行緒執行完畢,thread會銷燬internal copy。對於這一點和boost::bind也一樣,boost::bind也會拷貝引數。至於上面程式出現的那麼多拷貝和析構輸出已經我已無力解釋了sorry。

情形4: 有些時候需要向執行緒傳遞一個引用,由執行緒執行一些計算最後這個計算結果將在主執行緒中使用,可以用boost::ref傳遞一個引用到執行緒空間。

#include<iostream>
#include<string>
#include<boost/thread.hpp>
#include<boost/ref.hpp>
#include<unistd.h>
using namespace std;
class test{
    public:
        test(int i=0):data(i){}
        test(const test& one){
            data=one.data;
            cout<<"test::copy constructor"<<endl;
        }
        test& operator=(const test& one){
            data=one.data;
            cout<<"test::operator=()"<<endl;
            return *this;
        }
        ~test(){
            cout<<"test::destructor"<<endl;
        }
    public:
        int data;
};
void func(test& one){
    cout<<"func get the data "<<one.data++<<endl;
}
void oops(){
    test one(10);
    boost::thread t(func,boost::ref(one));
    t.join();
    cout<<"oops() get the data "<<one.data<<endl;
}
int main(){
    oops();
    return 0;
}

程式輸出:

func get the data 10          //子執行緒修改資料
oops() get the data 11     //主執行緒將得到子執行緒修改後的結果,因為boost::ref是傳遞引用,沒有拷貝構造之類的
test::destructor

情形5:thread傳遞函式物件

#include<iostream>
#include<boost/thread.hpp>
using namespace std;
class test{
    public:
        test(int i=0):data(i){}
        ~test(){
            cout<<"test::destructor"<<endl;
        }
        test(const test& one){
            data=one.data;
        }
        test& operator=(const test& one){
            data=one.data;
            return *this;
        }
        void operator()(){
            cout<<"test::operator() "<<data<<endl;
        }
        void show(){
            cout<<"test::show() "<<data<<endl;
        }
    public:
        int data;
};
void oops(){
    boost::thread t((test()));
    t.join();
}
int main(){
    oops();
    return 0;
}
程式輸出:

test::destructor                 //函式物件沒有產生copy constructor
test::destructor
test::destructor
test::operator() 0
test::destructor

情形6:仿照boost::bind將物件的成員函式作為執行緒函式

#include<iostream>
#include<boost/thread.hpp>
using namespace std;
class test{
    public:
        test(int i=0):data(i){}
        ~test(){
            cout<<"test::destructor"<<endl;
        }
        test(const test& one){
            data=one.data;
        }
        test& operator=(const test& one){
            data=one.data;
            return *this;
        }
        void operator()(){
            cout<<"test::operator() "<<data<<endl;
        }
        void show(){
            cout<<"test::show() "<<data<<endl;
        }
    public:
        int data;
};
void oops(){
    test one(10);
    boost::thread my_thread(&test::show,&one);
    my_thread.join();
}
int main(){
    oops();
    return 0;
}

程式輸出:

test::show() 10             
test::destructor

     說明:此時thread的執行緒將呼叫one::show(),thread將&one當做物件地址傳遞到執行緒中。

情形7:向thread傳遞指標:普通指標和智慧指標。

#include<iostream>
#include<boost/thread.hpp>
#include<boost/shared_ptr.hpp>
using namespace std;
class test{
    public:
        test(int i=0):data(i){}
        ~test(){
            cout<<"test::destructor"<<endl;
        }
        test(const test& one){
            data=one.data;
        }
        test& operator=(const test& one){
            data=one.data;
            return *this;
        }
    public:
        int data;
};
void fun(boost::shared_ptr<test> ptr){
    ptr->data++;
}
void fun_(test* ptr){
    ptr->data++;
}
void oops(){
    boost::shared_ptr<test> ptr(new test(10));
    boost::thread my_thread(fun,ptr);
    my_thread.join();
    cout<<"shared_ptr "<<ptr->data<<endl;
    test* one=new test(10);
    boost::thread t(fun_,one);
    t.join();
    cout<<"test* "<<one->data<<endl;
    delete one;
}
int main(){
    oops();
    return 0;
}

程式輸出:

shared_ptr 11         //智慧指標
test* 11                     //普通指標
test::destructor
test::destructor

            說明:傳遞指標給執行緒函式,thread拷貝的是指標物件,故執行緒對指標所指物件的修改將會影響到主執行緒中的物件。注意的是智慧指標可能會引起物件生命週期的延長,若thread::detach那麼智慧指標肯定比普通裸指標安全,特別是detach後oops退出,智慧指標管理的物件由於引用計數不會被析構,而普通裸指標由於oops退出析構了局部物件導致dangle pointer。

情形8:std::unique_ptr的movable語義傳遞引數

void process_big_object(std::unique_ptr<big_object>);
std::unique_ptr<big_object> p(new big_object);
p->prepare_data(42);
std::thread t(process_big_object,std::move(p));//為什麼一定要用std::move?std::unique_ptr是個左值物件,std::move的功能是將左值轉為右值使用
  說明:std::unique_ptr不能共享所有權,但是可以轉移所有權,採用std::move()語義後原來的std::unique_ptr將為NULL。