Loading... # 一、c++11 thread源码分析 ```cpp class thread { // class for observing and managing threads public: //.... template <class _Fn, class... _Args, enable_if_t<!is_same_v<_Remove_cvref_t<_Fn>, thread>, int> = 0> explicit thread(_Fn&& _Fx, _Args&&... _Ax) { using _Tuple = tuple<decay_t<_Fn>, decay_t<_Args>...>; auto _Decay_copied = _STD make_unique<_Tuple>(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...); constexpr auto _Invoker_proc = _Get_invoke<_Tuple>(make_index_sequence<1 + sizeof...(_Args)>{}); _Thr._Hnd = reinterpret_cast<void*>(_CSTD _beginthreadex(nullptr, 0, _Invoker_proc, _Decay_copied.get(), 0, &_Thr._Id)); } }; ``` 线程的函数传递是基于模板函数,任意类型参数都传入,并对参数做一份拷贝。回调函数完成之后,会进行释放。 # 二、线程函数入口如何传递参数 ## 1、普通类型传递 ```cpp #include <iostream> #include <thread> using namespace std; using namespace chrono; using namespace this_thread; void ThreadMain(int p1, float p2, string str) { this_thread::sleep_for(chrono::milliseconds(100)); cout << "ThreadMain" << p1 << " "<< p2 << str << endl; } int main() { thread th; { float f1 = 12.1f;//f1释放掉是否会有影响 //所有的参数做赋值 th = thread(ThreadMain, 101, f1, "test string para"); } th.join(); return 0; } $ ./2_3 ThreadMain101 12.1test string para ``` float 和 string类型都是可以正常传递。因为线程thread里面会做一份拷贝 ## 2、对象作为类型传递 ```cpp #include <iostream> #include <thread> #include <string> using namespace std; using namespace chrono; using namespace this_thread; class Para { public: Para(){cout << "construct Para"<<endl;} Para(const Para&p ){cout << "Copy Para"<<endl;}//这里必须加const 否则会编译不过 ~Para(){cout << "descontruct Para"<<endl;} string name; }; void ThreadMain(int p1, float p2, string str, Para p4) { this_thread::sleep_for(chrono::milliseconds(100)); cout << "ThreadMain" << p1 << " "<< p2 << str << p4.name<<endl; } int main() { thread th; { float f1 = 12.1f;//f1释放掉是否会有影响 Para p; p.name = "test Para class"; //所有的参数做赋值 th = thread(ThreadMain, 101, f1, "test string para", p); } th.join(); return 0; } $ g++ 2_3.cpp -o 2_3 -std=c++11 -lpthread $ ./2_3 construct Para Copy Para Copy Para descontruct Para descontruct Para Copy Para ThreadMain101 12.1test string para descontruct Para descontruct Para ``` 一次构造+3次拷贝构造+4次析构 应该考虑使用引用的方式来传递。 question: 如果是引用的话,变量以及被释放掉了是否会有影响? ## 3、指针作为类型传递 ```cpp #include <iostream> #include <thread> #include <string> using namespace std; using namespace chrono; using namespace this_thread; class Para { public: Para(){cout << "construct Para"<<endl;} Para(const Para&p ){cout << "Copy Para"<<endl;}//这里必须加const 否则会编译不过 ~Para(){cout << "descontruct Para"<<endl;} string name; }; // 线程入口函数 void ThreadMain1(Para* pstr) { this_thread::sleep_for(chrono::milliseconds(100)); //str 已经销毁 cout << "ThreadMainPtr name = " << pstr->name << endl; } int main() { //传递线程指针 thread th; { Para p; p.name = "test ThreadMain1 name"; //所有的参数做赋值 thread th(ThreadMain1, &p); th.join();//在这个作用域里面等,所以不会存在变量提前释放的情况 } //th.join(); return 0; } peter@iZbp1aq6c9kbbder4q405mZ:~/test/threadTopic/class_demo/dierzhang$ ./2_3 construct Para ThreadMainPtr name = test ThreadMain1 name descontruct Para ``` 没有产生构造函数。 ### 易错点 ```cpp #include <iostream> #include <thread> #include <string> using namespace std; using namespace chrono; using namespace this_thread; class Para { public: Para(){cout << "construct Para"<<endl;} Para(const Para&p ){cout << "Copy Para"<<endl;}//这里必须加const 否则会编译不过 ~Para(){cout << "descontruct Para"<<endl;} string name; }; // 线程入口函数 void ThreadMain1(Para* pstr) { this_thread::sleep_for(chrono::milliseconds(100)); //str 已经销毁 cout << "ThreadMainPtr name = " << pstr->name << endl; } int main() { //Para释放 不正常的版本 Para提前被释放了 { Para p; p.name = "test ThreadMain1 name"; //所有的参数做赋值 thread th1(ThreadMain1, &p); th1.detach(); } getchar();//这个场景要加这句话 防止主线程提前退出后,来不及执行子线程 return 0; } peter@iZbp1aq6c9kbbder4q405mZ:~/test/threadTopic/class_demo/dierzhang$ ./2_3 construct Para descontruct Para ThreadMainPtr name = ⚌⚌V name 1//自己输入的 以便让线程结束 ``` Para p; 已经释放,子线程再去访问已释放的资源。 ## 4、引用作为类型传递 ```cpp #include <iostream> #include <thread> #include <string> using namespace std; using namespace chrono; using namespace this_thread; class Para { public: Para(){cout << "construct Para"<<endl;} Para(const Para&p ){cout << "Copy Para"<<endl;}//这里必须加const 否则会编译不过 ~Para(){cout << "descontruct Para"<<endl;} string name; }; // 线程入口函数 void ThreadMainRef(Para& str) { this_thread::sleep_for(chrono::milliseconds(100)); cout << "ThreadMainRef name = " << str.name << endl; } int main() { //传递线程指针 正常的版本 { //引用传递 Para p; p.name = "test ref"; thread th(ThreadMainRef, ref(p)); th.join(); } return 0; } ``` thread th(ThreadMainRef, ref(p)); 必须加上ref,否则会编译报错 ### 易错点 ```cpp #include <iostream> #include <thread> #include <string> using namespace std; using namespace chrono; using namespace this_thread; class Para { public: Para(){cout << "construct Para"<<endl;} Para(const Para&p ){cout << "Copy Para"<<endl;} ~Para(){cout << "descontruct Para"<<endl;} string name; }; // 线程入口函数 void ThreadMainRef(Para& str) { this_thread::sleep_for(chrono::milliseconds(100)); cout << "ThreadMainRef name = " << str.name << endl; } int main() { //Para释放 不正常的版本 Para提前被释放了 { //引用传递 Para p; p.name = "test ref"; thread th(ThreadMainRef, ref(p));//不加ref的话就会去找普通的传参,和模板有关 th.detach(); } getchar(); return 0; } peter@iZbp1aq6c9kbbder4q405mZ:~/test/threadTopic/class_demo/dierzhang$ ./2_3 construct Para descontruct Para ThreadMainRef name = test ref 1 ``` Para提前被释放了,但是这里依旧可以正常打印,原因就是? ## 5、成员函数作为类型传递 ```cpp #include <iostream> #include <thread> #include <string> using namespace std; using namespace chrono; using namespace this_thread; class MyThread { public: //线程入口函数 void Main() { cout << "MyThread Main " <<name<< endl; } string name; int age = 10; }; int main() { { MyThread myth; myth.name = "test mythread"; thread th(&MyThread::Main,&myth); th.join(); } return 0; } peter@iZbp1aq6c9kbbder4q405mZ:~/test/threadTopic/class_demo/dierzhang$ ./2_3 MyThread Main test mythread ``` ## 6、封装一个线程 ```cpp #include <iostream> #include <thread> #include <string> using namespace std; using namespace chrono; using namespace this_thread; class Para { public: Para(){cout << "construct Para"<<endl;} Para(const Para&p ){cout << "Copy Para"<<endl;}//这里必须加const 否则会编译不过 ~Para(){cout << "descontruct Para"<<endl;} string name; }; // 线程入口函数 void ThreadMain1(Para* pstr) { this_thread::sleep_for(chrono::milliseconds(100)); //str 已经销毁 cout << "ThreadMainPtr name = " << pstr->name << endl; } // 线程入口函数 void ThreadMainRef(Para& str) { this_thread::sleep_for(chrono::milliseconds(1000)); //str 已经销毁 cout << "ThreadMainRef name = " << str.name << endl; } class MyThread { public: //线程入口函数 void Main() { cout << "MyThread Main " <<name<< endl; } string name; int age = 10; }; class IThread { public: virtual void Start() { isExit = false; th_ = std::thread(&IThread::Main, this); } virtual void Stop() { isExit = true; Wait(); } virtual void Wait() { if(th_.joinable())//当可以join的时候才join th_.join(); } bool isExitStatus() { return isExit; } private: virtual void Main() = 0; bool isExit = false; std::thread th_; }; class Thread : public IThread { public: void Main() override { cout << "Thread::Main" <<endl; while(!isExitStatus()) { this_thread::sleep_for(chrono::milliseconds(100)); cout << "."<<flush;//要刷新,否则是先输出到缓冲区中 } cout << "Thread::Main end" <<endl; } string name; }; int main() { { //使用封装的线程 Thread testThread; testThread.name = "Thread name"; testThread.Start(); sleep_for(seconds(3)); testThread.Stop(); //testThread.Wait();//等待子线程退出 //getchar(); } getchar(); //th.join(); return 0; } ``` 代码执行好像有点问题 ## 7、lambda 临时函数作为线程入口函数 ### 7.1、lambda 线程示例: 我们需要在**每个线程上添加一个标记**。鉴于我们使用lambda,所以我们可以尝试下它的捕获能力。 通过将i的值传递给线程,使用[i]我们可以将索引传递到线程函数中: ```cpp thread t([](int i) { cout << "Hello World from lambda thread."<<i << endl; },123); ``` 在此基础上我们将创建5个线程,然后把线程放进一个vector容器中, 用for\_each()完成线程的汇合(join): ```jsx #include <iostream> #include <thread> #include <vector> #include <algorithm> int main() { // vector 容器存储线程 std::vector<std::thread> workers; for (int i = 0; i < 5; i++) { workers.push_back(std::thread([]() { std::cout << "thread function\\n"; })); } std::cout << "main thread\\n"; // 通过 for_each 循环每一个线程 // 第三个参数赋值一个task任务 // 符号'[]'会告诉编译器我们正在用一个匿名函数 // lambda函数将它的参数作为线程的引用t // 然后一个一个的join std::for_each(workers.begin(), workers.end(), [](std::thread &t) { t.join(); }); return 0; } 执行结果: thread function thread function thread function thread function thread function main thread ``` 可参考:[https://www.jb51.net/article/155157.htm](https://www.jb51.net/article/155157.htm) ### 7.2、线程入口为类成员Lambda函数 ```cpp #include <iostream> #include <thread> #include <string> using namespace std; using namespace chrono; using namespace this_thread; class TestLambda { public: void Start() { thread t([this]() { cout << "TestLambda thread this."<<name << endl; }); t.join(); } string name = "test lambda"; }; int main() { //使用Lambda来处理 TestLambda th; th.Start(); return 0; } peter@iZbp1aq6c9kbbder4q405mZ:~/test/threadTopic/class_demo/dierzhang$ ./2_3 TestLambda thread this.test lambda ``` ### 7.3、call\_once 多线程调用函数,但函数只进入一次 ```jsx void SystemInit() { cout << "Call SystemInit" << endl; } void CallOnceSystemInit() { static std::once_flag flag; std::call_once(flag, SystemInit); } ``` 最后修改:2025 年 07 月 02 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏