簡述
- 如何啟動(dòng)線程
因啟動(dòng)性程需要一個(gè)callable type,可以有函數(shù)指針绕娘,類重載()脓规,lambda三種形式。
其中類重載()险领,lambda侨舆,有可能會(huì)拿到非運(yùn)行程序的變量,要小心绢陌。
啟動(dòng)完成后挨下,需要detach或join,否則程序運(yùn)行會(huì)報(bào)錯(cuò)脐湾。引入一個(gè)thread_guard類臭笆。 - 給線程傳參數(shù),會(huì)進(jìn)行拷貝操作。
如果是引用秤掌,則要小心變量的作用域愁铺。他只會(huì)進(jìn)行拷貝操作,而不會(huì)進(jìn)行轉(zhuǎn)換操作闻鉴。
如果是類茵乱,那么又不需要std::ref。
如果是moveonly的值孟岛,在傳進(jìn)去之前要std::move - thread 是一個(gè) moveonly的類型瓶竭,這里介紹了scoped_thread (它是一個(gè)線程的'子類')
還有督勺,當(dāng) thread t1(xxxx); thread t2(x);
t2 = std::move(t1);會(huì)報(bào)錯(cuò),因?yàn)閠1不見了斤贰。
(經(jīng)過測試玷氏,沒有辦法再join了,如果有join過腋舌,則不會(huì)報(bào)錯(cuò)) - 其它
獲取當(dāng)前cpu的核數(shù)
獲取當(dāng)前線程的id - 測試代碼
摘錄
-
啟動(dòng)線程的方式
-
函數(shù)
void do_some_work(); std::thread my_thread(do_some_work);
-
重載operator()
經(jīng)過測試盏触,發(fā)現(xiàn)不管用哪種方式都會(huì)調(diào)用 移動(dòng)拷貝構(gòu)造函數(shù)
只是第一種會(huì)調(diào)用拷貝構(gòu)造函數(shù)一次,移動(dòng)拷貝構(gòu)造函數(shù)一次块饺。別的都是調(diào)用兩次移動(dòng)拷貝構(gòu)造函數(shù)class background_task{ public: void operator()()const{ //..... } }; background_task f; std::thread my_thread(f)//書中講會(huì)執(zhí)行background_task這個(gè)類的拷貝構(gòu)造函數(shù) //----- 或者 std::thread my_thread(background_task())//書中講會(huì)執(zhí)行background_task這個(gè)類的移動(dòng)拷貝構(gòu)造函數(shù) std::thread my_thread{background_task()}//書中講會(huì)執(zhí)行background_task這個(gè)類的移動(dòng)拷貝構(gòu)造函數(shù)
-
lambda
std::thread my_thread([](){ //.. });
-
thread_guard
class thread_guard{ std::thread &t; public: explicit thread_guard(std::thread &t_):t(t_){} ~thread_guard(){ if ( t.joinable() ){ t.join(); } } thread_guard(thread_guard const&) = delete; thread_guard& operator=(thread_guard const&) = delete; }; //如何使用 struct func; void f(){ int some_local_state=0; func my_func(some_local_state); std::thread t(my_func); thread_guard g(t); //會(huì)自動(dòng)join do_something_in_current_thread(); }
-
-
傳參數(shù)進(jìn)去
-
傳參赞辩,注意作用域
經(jīng)過測試程序并沒有崩潰,但是struct func的 int &i這個(gè)值的數(shù)據(jù)確實(shí)是跟 some_local_state 的地址一樣授艰。
所以辨嗽,其實(shí)是有問題的 ,只是暫時(shí)沒崩而已淮腾。struct func{ int &i; func(int &i_):i(i_){} operator()(){ for ( int j=0; j<1000000;j++ ){ dosomething(i); } } }; void oops(){ int some_local_state=0; func my_func(some_local_state); std::thread my_thread(my_func); //在 thread里運(yùn)行的 &i 數(shù)據(jù)會(huì)沒掉 my_thread.detach(); }
//std::thread t(func,args...) //args默認(rèn)會(huì)進(jìn)行拷貝操作 是的糟需,會(huì)有 void f(int i, const std::string &str); //如果是 std::string str 呢? 應(yīng)該也一樣谷朝,因?yàn)閱栴}并沒有解決 void oops(int somevalue){ char buff[10] = {0}; sprintf(buff,"%d",somevalue); std::thread t(f,somevalue,buff); //在這里buff會(huì)從char[]變成 std::string tmp //但不知道是何時(shí)操作這個(gè)轉(zhuǎn)換洲押,所以有可能是oops已經(jīng)運(yùn)行完了, //所以buff值就不知道變成什么樣了圆凰。 //解決方法:std::thread t(f,somevalue,std::string(buff)); t.detach(); }
當(dāng)你想把引用傳進(jìn)去杈帐,并在thread里面修改值且期望在外面這個(gè)值也會(huì)有作用,那么:
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并沒有改變 //原因:std::thread ( func,... ) ...是會(huì)把那些參數(shù)弄一個(gè)temp然后再傳進(jìn)去 (試試是不是會(huì)調(diào)copy函數(shù)) //解決方法: std::thread t(update_data_for_widget,w,std::ref(data)); }
- 如果是一個(gè)類和成員函數(shù) my_x 不會(huì)執(zhí)行拷貝
class X{ public: void do_xxx_work(); }; X my_x; std::thread t(&X::do_xxx_work,&my_x);
- 如果一個(gè)參數(shù)是moveonly的专钉,比如:unique_ptr 需要把那個(gè)ptr 給move掉
void process_big_object(std::unique_ptr<big_object) p); std::unique_ptr<big_object> p(new big_object); p->prepare_data(42); std::thread t(process_big_object,std::move(p));
-
-
thread 是一個(gè)move only的值
-
scoped_thread
這個(gè)例子放在這里挑童,是因?yàn)橐猻td::move 這也是跟thread_guard的區(qū)別
thread_guard是傳引用過去。class scoped_thread{ std::thread t; public: explicit scoped_thread(std::thread t_):t(std::move(t_)){ if ( !t.joinable() ){ throw::std::logic_error("no thread"); } } ~scoped_thread(){ t.join(); } scoped_thread(scoped_thread const &)=delete; scoped_thread& operator=(scoped_thread const &)=delete; }; //如何使用 struct func; void f(){ int some_local_state; scoped_thread t(std::thread(func(some_local_state))); do_something_in_current_thread(); }
-
不過如果有 n個(gè)thread
void do_work(unsigned id); void f(){ std::vector<std::thread> ths; for ( i=0;i<20; i++ ){ ths.push_pack(std::thread(do_work,i)); } std::for_each(ths.begin(),ths.end(),std::mem_fn(&std::thread::join)) }
-
可以認(rèn)為thread是只能移動(dòng)的,他比只能移動(dòng)還多一個(gè)限制
void func(); void other_func(); std::thread t1(func); std::thread t2(other_func); t1 = std::move(t2); //這時(shí)還會(huì)報(bào)錯(cuò)跃须,因?yàn)樵瓉淼膖1站叼,無法join
-
當(dāng)作為函數(shù)的返回值,是允許的 (詳細(xì)可以去查看RVO)
std::thread f(){ void some_func(); return std::thread(some_func); } std::thread g(){ void some_func(); std::thread t(some_func); return t; }
-
當(dāng)函數(shù)的參數(shù)為thread 時(shí) 如果是臨時(shí)變量需要move
void f(std::thread t); //怎么傳參: void g(){ //1 void some_func(); f(std::thread(some_func)); //2 std::thread t(some_func); f(std::move(t)); }
-
附:golang 的 defer很好用,來個(gè)c++版的
template <typename F> struct privDefer { F f; privDefer(F fp) : f(fp) {} ~privDefer() { f(); } }; template <typename F> privDefer<F> defer_func(F f) { return privDefer<F>(f); } #define BUFFALO_DEFER_1(x, y) x##y #define BUFFALO_DEFER_2(x, y) BUFFALO_DEFER_1(x, y) #define BUFFALO_DEFER_3(x) BUFFALO_DEFER_2(x, __COUNTER__) #define defer(code) auto BUFFALO_DEFER_3(_glngbll_defer_val_) = defer_func([&]() { code; }) //-------------- 使用 int main(){ int *a = new int; defer(delete a); return 0; }
-
-
雜項(xiàng)
- 獲取此機(jī)器能并行處理多少線程 等于cpu的核數(shù)
std::thread::hard_concurrency();
- 獲取此機(jī)器能并行處理多少線程 等于cpu的核數(shù)
2. 獲取thread id
```
//獲取id 類型是 std::thread::id;
std::thread::id master_id;
if ( std::this_thread::get_id() == master_id){
}
```
- 測試代碼
-
測試拷貝多少次
#include <iostream> #include <thread> typedef struct Big_t { Big_t() = default; Big_t(const Big_t &other) { std::cout << "Big_t" << std::endl; } void func() {} int v[100]; } Big_t; void funcReference(const Big_t &big) {} void funcValue(Big_t big) {} int main(int argc, const char *argv[]) { Big_t big; std::cout << "------------------std::ref 沒有construct" << std::endl; std::thread t(funcReference, std::ref(big)); t.join(); std::cout << "------------------2次construct" << std::endl; std::thread t1(funcReference, big); t1.join(); std::cout << "funcValue ------------------std::ref 1次" << std::endl; std::thread t10(funcValue, std::ref(big)); t10.join(); std::cout << "------------------ 三次" << std::endl; std::thread t11(funcValue, big); t11.join(); std::cout << "------------------ class 沒有" << std::endl; std::thread t2(&Big_t::func, &big); t2.join(); return 0; }
-
測試拷貝函數(shù)和移動(dòng)拷貝函數(shù)
#include <iostream> #include <thread> class background_task { public: background_task() = default; background_task(const background_task &other) { std::cout << "background_task_construct 1" << std::endl; } background_task &operator=(const background_task &other) { std::cout << "background_task_construct 2" << std::endl; return *this; } background_task(background_task &&other) { std::cout << "background_task_construct 3" << std::endl; } background_task &operator=(background_task &&other) { std::cout << "background_task_construct 4" << std::endl; return *this; } virtual ~background_task() = default; public: void operator()() const { std::cout << "thread func" << std::endl; } }; int main(int argc, const char *argv[]) { { background_task f; std::cout << "thread t(f)" << std::endl; std::thread t(f); t.join(); } std::cout << "-----------------" << std::endl; { std::cout << "thread t(background_task)" << std::endl; std::thread t((background_task())); t.join(); } std::cout << "-----------------" << std::endl; { std::cout << "thread t{background_task()}" << std::endl; std::thread t{background_task()}; t.join(); } std::cout << "-----------------" << std::endl; return 0; }
-
看move only的值是否會(huì)因沒有任何值關(guān)聯(lián)時(shí)會(huì)報(bào)錯(cuò)
#include <iostream> #include <thread> void func() { std::cout << "func" << std::endl; } void otherfunc() { std::cout << "otherfunc" << std::endl; } int main(int argc, const char *argv[]) { std::thread t1(func); t1.join(); std::thread t2 = std::move(t1); t1 = std::thread(otherfunc); t1.join(); std::thread t3; t3 = std::move(t2); t1 = std::move(t3); return 0; }
-