1. 程式人生 > >管理線程之向線程函數傳遞參數

管理線程之向線程函數傳遞參數

track pan prepare dsm tac 變量 函數調用 char ref

向線程函數傳遞參數在構造線程對象時就可以完畢。可是要記住,默認情況下是把參數復制到線程內部,即使在函數中使用的是引用。比如

void f(int i,std::string const &s);
std::thread t(f,3,"hello");
上面代碼中,函數f的第二個參數是std::string,傳遞的是char const *會轉換為string。

當使用指針指向自己主動變量時。要特別註意:

void f(int i, std::string const& s);
void oops(int some_param)
{
	char buffer[1024];
	sprintf(buffer,"%i",some_param);
	std::thread t(f,3,buffer);
	t.detach();
}
在這樣的情況下,指針指向局部變量buffer,然後傳遞到新創建的線程。

非常可能會發生函數oops已經終止。可是buffer還沒有轉換為std::string。這是buffer已經銷毀。解決辦法就是在傳遞之前就轉換:

void f(int i, std::string const& s);
void oops(int some_param)
{
	char buffer[1024];
	sprintf(buffer,"%i",some_param);
	std::thread t(f,3,std::string(buffer));
	t.detach();
}
這是,依賴buffer想std::string的隱式轉換,之後作為函數參數

可能會出現和上面相反的情況:線程拷貝了對象實例,可是你想要傳遞引用。在使用引用傳遞參數。線程更新數據時:

void update_data_for_widget(widget_id w,widget_data& data);

void oops_again(widget_id w)
{
	widget_data data;
	std::thread t(update_data_for_widget,w,data);
	display_status();
	t.join();
	process_widget_data(data);
}
雖然update_data_for_widget希望第二個參數用引用傳遞,可是std::thread的構造函數不知道,這是函數的參數會被拷貝。當調用update_data_for_widget時,會傳遞拷貝data的引用,而不是data的引用。當線程終止,線程內部的拷貝析構,可是函數process_widget_data傳遞的是未更新的data。對於熟悉std:bind的人來說,立即就能想到解決的辦法:你須要使用std::ref包裝參數。須要更改線程創建形式為:

std::thread t(update_data_for_widget,w,std::ref(data));
假設你熟悉std::bind,參數傳遞的語法就easy理解。

由於std::thread的構造函數和std::bind都是使用相同的原理。這也就是說,你能夠傳遞成員函數的指針作為函數參數,假如你使用對象指針作為第一個參數。

class X
{
	public:
		void do_lengthy_work();
};
X my_x;
std::thread t(&X::do_length_work,&my_x);
上面代碼會在新線程調用my_x.do_lengthy_work()。由於my_x的地址作為對象指針。你也能夠提供參數比如成員函數調用:std::thread的第三個參數將會成為成員函數的第一個參數。

另一種情況是函數參數對象不能拷貝,僅僅能轉移其全部權(比如STL中的auto_ptr指針)。std::unique_ptr就是這種一個樣例。std::unique指針一次僅僅能指向一個對象。當指針析構時。對象也就被析構了。在賦值時是轉移全部權(像auto_ptr)。

在使用時,當對象是暫時對象時,會自己主動調用move,當是個變量時必須調用move。

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));

管理線程之向線程函數傳遞參數