【Linux】一文带你了解C++中的多线程及其底层逻辑(thread、join、封装线程 ...

鼠扑  金牌会员 | 2025-1-25 06:08:38 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 918|帖子 918|积分 2754

绪论​

每日鼓励:“不设限和自我肯定的心态:I can do all things。 — Stephen Curry”
    绪论​:
本章是线程的第三篇章,前两章带你了解了线程以及线程原生库中的操纵,本章主要结合前两章的根本,学习入门c++中的线程函数,了解其底层是什么,并且自己封装一个线程库来使用并巩固。
————————
早关注不迷路,话不多说安全带系好,发车啦(建议电脑观看)。

  站在语言角度再理解pthread库

C++中的多线程


  • 创建线程:thread
  • 等候线程:join
  1. #include<unistd.h>
  2. #include<thread>
  3. #include<iostream>
  4. using namespace std;
  5. void myrun()
  6. {
  7.         while(true)
  8.         {
  9.                 cout << "i am thread" << endl;
  10.                 sleep(1);
  11.         }
  12. }
  13. int main()
  14. {
  15.         thread t(myrun);
  16.         t.join();
  17.        
  18.         return 0;
  19. }
复制代码
其中使用到的c++中封装的线程函数 thread、join,他们在编译的时候纵然看起来没有使用到原生线程库,但同样要加-lpthread,由于c++的多线程本质上就是封装了Linux(或Windows)的多线程,所以同样要包括pthread库。
也就是说:c++的库中的函数本质是对pthread库中体系调用函数(pthread_create)的封装(C++11内部的多线程,本质就是对原生线程库的封装)
线程属性的局部存储

含线程代码中的全局变量,在多个线程中是共享的。
代码:
  1. #include<iostream>
  2. #include<unistd.h>
  3. #include<cstdlib>
  4. #include<string>
  5. #include<pthread.h>
  6. #include <thread>
  7. using namespace std;
  8. int g_val = 100;
  9. void* ThreadRountine(void * args)
  10. {
  11.     string name = static_cast<const char *>(args);
  12.     while(true)
  13.     {
  14.         sleep(1);
  15.         cout << name <<": g_val:" << g_val << " &g_val: " << &g_val << endl;
  16.         g_val--;
  17.     }
  18.     return nullptr;
  19. }
  20. int main()
  21. {
  22.     pthread_t tid;
  23.     pthread_create(&tid,nullptr,ThreadRountine,(void*)"thread1");
  24.     while(true)
  25.     {
  26.         sleep(1);
  27.         cout << "main thread:"<<" g_val: " << g_val << " &g_val: " << &g_val << endl;
  28.     }
  29.     pthread_join(tid,nullptr);
  30. }
复制代码
此处线程中举行了递减操纵,发现外部主线程中同样也减少了:也就说明他们是共享的资源!

__thread

线程的局部存储:当给全局变量前加上__thread编译选项,这样就能实现线程的局部存储,每个线程都有自己的该全局变量。
留意点:__thread只能针对内置类型
  1. __thread int g_val = 100;
复制代码

上面的g_val就各自有一份了!
注:
线程中可以举行fork吗?虽然是可以在多线程程序中调用 fork(),但这种做法可能会导致程序的行为不稳定。尤其是如果你在多线程中使用 fork() 然后调用 exec() 来替换程序,可能会破坏多线程环境,由于 exec() 会终止全部线程。因此,除非非常必要,否则在多线程环境中使用 fork() 一般是不推荐的,特别是如果你计划举行进程替换。

模仿实现封装c++中的线程:

根据之前所知:c++的线程库底层本质就是封装了原始线程库的,所以自己也能通过面向对象的封装来自己实现,从而让自己更加了解
(很多细节请看解释)
thread.hpp:
  1. #pragma once
  2. #include<iostream>
  3. #include<string>
  4. #include<functional>
  5. #include<pthread.h>
  6. using namespace std;
  7. //typedef function<void()> func_t
  8. using func_t = function<void()>;//返回值void 参数为空;
  9. class Thread
  10. {
  11. public:
  12.     Thread(func_t func,const  string& name)
  13.     :_tid(0),_name(name),_isrunning(false),_func(func)
  14.     {}
  15. //因为在类内的函数默认是有this指针的这样就会导致pthread_create的threadrotine的类型不匹配而导致的无法传参
  16. //所以解决方法就是改成静态函数,但此时又不能使用成员变量了
  17. //所哟把参数args改成this传递进来!
  18.     static void *ThreadRotine(void* args)
  19.     {
  20.         Thread *ts = static_cast<Thread*>(args);//安全的类型转换
  21.         ts->_func();//执行函数
  22.         return nullptr;
  23.     }
  24.     bool Start()
  25.     {
  26.         int n = pthread_create(&_tid,nullptr,ThreadRotine,this);//创建线程
  27.         if(n == 0)
  28.         {
  29.             _isrunning = true;
  30.             return true;
  31.         }
  32.         else return false;
  33.     }  
  34.     string Threadname()
  35.     {
  36.         return _name;
  37.     }
  38.     bool Join()
  39.     {
  40.         if(!_isrunning) return true;
  41.         int n = pthread_join(_tid,nullptr);//等待线程
  42.         if(n==0)
  43.         {
  44.             _isrunning = false;
  45.             return true;
  46.         }
  47.         return false;
  48.     }
  49.     bool Isrunning()
  50.     {
  51.         return _isrunning;
  52.     }
  53.     ~Thread()
  54.     {}
  55. private:
  56.     string _name;
  57.     func_t _func;
  58.     pthread_t _tid;//创建自动形成
  59.     bool _isrunning;
  60. };
复制代码
main.cpp:
  1. #include<iostream>
  2. #include<thread>
  3. #include<cstdlib>
  4. #include "Thread.hpp"
  5. #include<unistd.h>
  6. #include<vector>
  7. using namespace std;
  8. string GetThreadname()
  9. {
  10.     static int number = 1;//本质就是全局变量
  11.     char name[64];
  12.     snprintf(name,sizeof(name),"thread-%d",number++);
  13.     return name;
  14. }
  15. void Print()
  16. {
  17.     while(true)
  18.     {
  19.         cout << "hello Linux"" << endl;
  20.         sleep(1);
  21.     }
  22. }
  23. int main()
  24. {
  25.     const int num = 5;
  26.     vector<Thread> threads;
  27.     for(int i = 0 ; i < num ;i++)
  28.     {
  29.         threads.push_back(Thread(Print,GetThreadname()));
  30.     }
  31. //此时并没有启动线程
  32.     for(auto &t : threads)
  33.     {
  34.         cout << t.Threadname() << " is,running:" << t.Isrunning() <<endl;//打印线程名称即运行状态
  35.     }
  36.     sleep(5);
  37.     for(auto &t:threads)
  38.     {
  39.         t.Start();
  40.     }
  41. //启动线程后
  42.     for(auto &t : threads)
  43.     {
  44.         cout << t.Threadname() << " is,running:" << t.Isrunning() <<endl;
  45.     }
  46.     for(auto &t : threads)
  47.     {
  48.         t.Join();
  49.     }
  50.     // Thread t(Print,GetThreadname());
  51.     // cout << "is thread running?: " << t.Isrunning() << endl;
  52.     // t.Start();
  53.     // cout << "is thread running?: " << t.Isrunning() << endl;
  54.     // t.Join();
  55.     return 0;
  56. }
复制代码
线程从未启动->启动->执行所对应的任务:

参加模板:写成能传参自界说函数和参数
  1. //thread.hpp
  2. #pragma once
  3. #include<iostream>
  4. #include<string>
  5. #include<functional>
  6. #include<pthread.h>
  7. using namespace std;
  8. //注意!!
  9. //此处的修改,因为带有模板所以后面用该类型变成fun_t<T>
  10. template<class T>
  11. using func_t = function<void(T)>;//返回值void 参数为;
  12. //加上模板,这个类型是给传进来的参数数据data的!
  13. template<class T>
  14. class Thread
  15. {
  16. public:
  17.     Thread(T data,func_t<T> func,const string& name)//此处从fun_t -> fun_t<T>
  18.     :_tid(0),_name(name),_isrunning(false),_func(func),_data(data)
  19.     {}
  20. //因为在类内的函数默认是有this指针的这样就会导致pthread_create的threadrotine的类型不匹配而导致的无法传参
  21. //所以解决方法就是改成静态函数,但此时又不能使用成员变量了,所哟把参数args改成this传递进来!
  22.     static void *ThreadRotine(void* args)
  23.     {
  24.         Thread *ts = static_cast<Thread*>(args);
  25.         ts->_func(ts->_data);
  26.         return nullptr;
  27.     }
  28.   //...和上面一样就省略了
  29. private:
  30.     string _name;
  31.     func_t<T> _func;//此处从fun_t -> fun_t<T>
  32.     pthread_t _tid;//创建自动形成
  33.     bool _isrunning;
  34.     T _data;
  35. };
  36. // ---------------------------------
  37. //main.cpp
  38. #include<iostream>
  39. #include<thread>
  40. #include<cstdlib>
  41. #include"Thread.hpp"
  42. #include<unistd.h>
  43. #include<vector>
  44. using namespace std;
  45. string GetThreadname()
  46. {
  47.     static int number = 1;//本质就是全局变量
  48.     char name[64];
  49.     snprintf(name,sizeof(name),"thread-%d",number++);
  50.     return name;
  51. }
  52. void Print(int num)
  53. {
  54.     while(num)
  55.     {
  56.         cout << "hello Linux: " << num-- <<  endl;
  57.         sleep(1);
  58.     }
  59. }
  60. int main()
  61. {
  62.     Thread<int> t(10,Print,GetThreadname());
  63.     t.Start();
  64.     t.Join();
  65.     return 0;
  66. }
复制代码

本章完。预知后事如何,暂听下回分解。
如果有任何题目欢迎讨论哈!
如果觉得这篇文章对你有所帮助的话点点赞吧!
连续更新大量Linux细致内容,早关注不迷路。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

鼠扑

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表