当前位置:   article > 正文

LINUX系统编程:线程控制

LINUX系统编程:线程控制

目录

1.铺垫

2.验证

3.线程的等待

4.线程退出


1.铺垫

LINUX内核中是不存在线程的概念的,只有轻量级进程的概念,但是在用户的角度来看,用户就是要把轻量级进程当做线程的使用,所以将轻量级进程的控制接口,封装成了一个线程库,用户直接使用库,也就屏蔽了底层的实现。

所以在g++编译的时候记得 加上 -lpthread选项,让可执行程序链接ptread库

2.验证

创建一个新的线程

参数thread,是输出型参数,创建进程的的id。

参数attr是一个包含创建时间,属性的结构体,用于初始化新创建的线程,设置为NULL采用默认初始化。

start_routine是一个函数指针,线程会执行函数的内容。

arg是start_routine的参数。

  1. #include <iostream>
  2. #include <pthread.h>
  3. #include <sys/types.h>
  4. #include <unistd.h>
  5. using namespace std;
  6. void *start_routine(void *v)
  7. {
  8. while (true)
  9. {
  10. cout << "I am new thread pid: " << getpid() << endl;
  11. sleep(1);
  12. }
  13. }
  14. int main()
  15. {
  16. pthread_t tid;
  17. pthread_create(&tid, nullptr, &start_routine, nullptr);
  18. while (true)
  19. {
  20. cout << "I am main thread pid:" << getpid() << endl;
  21. sleep(1);
  22. }
  23. return 0;
  24. }

我们发现这两线程的pid是同一个,这是因为他们属于同一个进程。

那操作系统调度的时候怎么区分他们两个呢?

别忘了,进程和线程在liunx下都叫轻量级进程LWP

让我们使用 ps -aL 选项查看进程的所有轻量级进程。

我们发现有两个LWP449636,449637,其中449636就是main函数的执行流,也就是说当进程内部之有一个执行流的时候PID==LWP。

3.线程的等待

关联一个结束的进程

参数thread是线程的id,这个id来源自pthread库,而不是系统内核的LWP,他们的关系是一 一对应的。

参数retval是一个输出型参数,用于保存目标线程的退出值。

4.线程退出

线程退出是不可以使用exit的,因为exit是给进程用的,使用exit直接整个进程就干掉了

1.return可以退出线程。

2.

终止调用的线程。

3.

终止参数thread线程

5.demo代码

v1

  1. #include <iostream>
  2. #include <pthread.h>
  3. #include <sys/types.h>
  4. #include <unistd.h>
  5. #include <vector>
  6. #include <string>
  7. #include <cstdio>
  8. #include <cstdlib>
  9. using namespace std;
  10. // 创建5个进程,分别让他们执行任务,获取返回的结果
  11. enum
  12. {
  13. sum = 5
  14. };
  15. void *handler(void *arg)
  16. {
  17. char* threadname = static_cast<char*>(arg);
  18. cout << "I am: " << threadname << endl;
  19. delete[] threadname;
  20. return (void *)1;
  21. }
  22. int main()
  23. {
  24. // 创建线程
  25. vector<pthread_t> tids;
  26. for (int i = 0; i < sum; i++)
  27. {
  28. sleep(1);
  29. char *buffer = new char[64];
  30. pthread_t tid;
  31. snprintf(buffer, 64, "thread %d", i + 1);
  32. pthread_create(&tid, nullptr, handler, buffer);
  33. tids.push_back(tid);
  34. }
  35. for (auto &t : tids)
  36. {
  37. void *ret;
  38. int n = pthread_join(t, &ret);
  39. cout << "main thread get ret:" << (long long)ret << " " << "n:" << n << endl;
  40. }
  41. return 0;
  42. }

v2

别忘了pthread_create最后一个参数类型是void* 这意味着可以和c++的类结合起来,不仅仅是传字符串,传整形,同样返回也可以使用类。

  1. #include <iostream>
  2. #include <pthread.h>
  3. #include <sys/types.h>
  4. #include <unistd.h>
  5. #include <vector>
  6. #include <string>
  7. #include <cstdio>
  8. using namespace std;
  9. // 创建5个进程,分别让他们执行任务,获取返回的结果
  10. enum
  11. {
  12. sum = 5
  13. };
  14. class result//储存结果
  15. {
  16. public:
  17. result()
  18. {
  19. }
  20. result(const string &threadname, int ret)
  21. : _threadname(threadname)
  22. , _ret(ret)
  23. {
  24. }
  25. void print()
  26. {
  27. cout << "I am " << _threadname << "added:" << _ret << endl;
  28. }
  29. private:
  30. string _threadname;
  31. int _ret;
  32. };
  33. class doTask//执行任务
  34. {
  35. public:
  36. doTask()
  37. {
  38. }
  39. doTask(int x, int y)
  40. : _x(x), _y(y)
  41. {
  42. }
  43. int run()
  44. {
  45. return _x + _y;
  46. }
  47. private:
  48. int _x;
  49. int _y;
  50. };
  51. class SentTask//发送任务
  52. {
  53. public:
  54. SentTask()
  55. {
  56. }
  57. SentTask(string &threadname, int x, int y)
  58. : _threadname(threadname)
  59. , dt(x, y)
  60. {
  61. }
  62. int Exute()
  63. {
  64. return dt.run();
  65. }
  66. string threadname()
  67. {
  68. return _threadname;
  69. }
  70. private:
  71. string _threadname;
  72. doTask dt;
  73. };
  74. void *handler(void *args)
  75. {
  76. SentTask *pst = static_cast<SentTask *>(args);
  77. result *ret = new result(pst->threadname(), pst->Exute());
  78. delete pst;
  79. return ret;
  80. }
  81. int main()
  82. {
  83. vector<pthread_t> tids;
  84. for (int i = 0; i < sum; i++)
  85. {
  86. pthread_t tid;
  87. char buffer[64];
  88. snprintf(buffer, 64, "thread %d", i + 1);
  89. string threadname = buffer;
  90. SentTask *st = new SentTask(threadname, i, 3);
  91. pthread_create(&tid, nullptr, handler, st);
  92. tids.push_back(tid);
  93. }
  94. for (auto t : tids)
  95. {
  96. void *ret;
  97. pthread_join(t, &ret);
  98. result *tmp = static_cast<result *>(ret);
  99. tmp->print();
  100. delete tmp;
  101. }
  102. return 0;
  103. }

5.补充

线程的优点

1.创建一个线程的代价比进程小的很多,创建一个线程就创建pcb,不用创建地址空间页表。

2.操作系统切换线程的工作,要比进程小的很多。如何理解呢?进程切换和线程切换相比,无非就是多了切换几个寄存器的工作呗,能有多大消耗呢?

但是别忘了还有缓存命中这种东西,cpu在加载代码的时候,会把该代码附近几行也加载到cpu的高速缓存中,一旦进程切换了,这些高速缓存的数据,对于下个进程就没有用了,都是失效的数据,但是对线程不是这样的,线程代码和全局数据是共享的。

线程的缺点

如果线程过多也是会有性能损失,因为线程来回切换也需要成本,在计算密集型应用线程数不要大于cpu核数。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号