简述
- 如何启动线程
因启动性程需要一个callable type,可以有函数指针,类重载(),lambda三种形式。
其中类重载(),lambda,有可能会拿到非运行程序的变量,要小心。
启动完成后,需要detach或join,否则程序运行会报错。引入一个thread_guard类。 - 给线程传参数,会进行拷贝操作。
如果是引用,则要小心变量的作用域。他只会进行拷贝操作,而不会进行转换操作。
如果是类,那么又不需要std::ref。
如果是moveonly的值,在传进去之前要std::move - thread 是一个 moveonly的类型,这里介绍了scoped_thread (它是一个线程的'子类')
还有,当 thread t1(xxxx); thread t2(x);
t2 = std::move(t1);会报错,因为t1不见了。
(经过测试,没有办法再join了,如果有join过,则不会报错) - 其它
获取当前cpu的核数
获取当前线程的id - 测试代码
摘录
-
启动线程的方式
-
函数
void do_some_work(); std::thread my_thread(do_some_work);
-
重载operator()
经过测试,发现不管用哪种方式都会调用 移动拷贝构造函数
只是第一种会调用拷贝构造函数一次,移动拷贝构造函数一次。别的都是调用两次移动拷贝构造函数class background_task{ public: void operator()()const{ //..... } }; background_task f; std::thread my_thread(f)//书中讲会执行background_task这个类的拷贝构造函数 //----- 或者 std::thread my_thread(background_task())//书中讲会执行background_task这个类的移动拷贝构造函数 std::thread my_thread{background_task()}//书中讲会执行background_task这个类的移动拷贝构造函数
-
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); //会自动join do_something_in_current_thread(); }
-
-
传参数进去
-
传参,注意作用域
经过测试程序并没有崩溃,但是struct func的 int &i这个值的数据确实是跟 some_local_state 的地址一样。
所以,其实是有问题的 ,只是暂时没崩而已。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里运行的 &i 数据会没掉 my_thread.detach(); }
//std::thread t(func,args...) //args默认会进行拷贝操作 是的,会有 void f(int i, const std::string &str); //如果是 std::string str 呢? 应该也一样,因为问题并没有解决 void oops(int somevalue){ char buff[10] = {0}; sprintf(buff,"%d",somevalue); std::thread t(f,somevalue,buff); //在这里buff会从char[]变成 std::string tmp //但不知道是何时操作这个转换,所以有可能是oops已经运行完了, //所以buff值就不知道变成什么样了。 //解决方法:std::thread t(f,somevalue,std::string(buff)); t.detach(); }
当你想把引用传进去,并在thread里面修改值且期望在外面这个值也会有作用,那么:
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,... ) ...是会把那些参数弄一个temp然后再传进去 (试试是不是会调copy函数) //解决方法: std::thread t(update_data_for_widget,w,std::ref(data)); }
- 如果是一个类和成员函数 my_x 不会执行拷贝
class X{ public: void do_xxx_work(); }; X my_x; std::thread t(&X::do_xxx_work,&my_x);
- 如果一个参数是moveonly的,比如:unique_ptr 需要把那个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 是一个move only的值
-
scoped_thread
这个例子放在这里,是因为要std::move 这也是跟thread_guard的区别
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个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)) }
-
可以认为thread是只能移动的,他比只能移动还多一个限制
void func(); void other_func(); std::thread t1(func); std::thread t2(other_func); t1 = std::move(t2); //这时还会报错,因为原来的t1,无法join
-
当作为函数的返回值,是允许的 (详细可以去查看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; }
-
当函数的参数为thread 时 如果是临时变量需要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很好用,来个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; }
-
-
杂项
- 获取此机器能并行处理多少线程 等于cpu的核数
std::thread::hard_concurrency();
- 获取此机器能并行处理多少线程 等于cpu的核数
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; }
-
测试拷贝函数和移动拷贝函数
#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的值是否会因没有任何值关联时会报错
#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; }
-