【Linux】深入明白线程控制

[复制链接]
发表于 3 天前 | 显示全部楼层 |阅读模式

个人主页~



一、线程等待的原理

pthread_join的作用是线程等待,此中retval参数通报线程退出状态的原理是:当目标线程结束时,pthread_join 会将目标线程的退出状态(即线程函数的返回值或 pthread_exit 通报的参数)存储在 *retval 所指向的内存位置,也就是说,pthread_join 会修改 retval 所指向的谁人 void * 范例变量的值
  1. #include <iostream>
  2. #include <unistd.h>
  3. #include <pthread.h>
  4. using namespace std;
  5. int g_val = 100;
  6. void *threadRoutine(void *args)
  7. {
  8.         //参数是线程名字,转化成字符串
  9.     const char *name = (const char *)args;
  10.     int cnt = 5;
  11.     while (true)
  12.     {
  13.             //线程打印线程pid,以及全局变量g_val和它的地址
  14.         printf("%s, pid: %d, g_val: %d, &g_val: 0X%p\n"
  15.                                         , name, getpid(),g_val, &g_val);
  16.         sleep(1);
  17.         cnt--;
  18.         if (cnt == 0)
  19.             break;
  20.     }
  21.         //线程退出,返回指针100
  22.     pthread_exit((void *)100);
  23. }
  24. int main()
  25. {
  26.     pthread_t pid;
  27.         //主线程id,线程属性设为无,新线程函数,新线程参数
  28.     pthread_create(&pid, nullptr, threadRoutine, (void *)"Thread 1");
  29.     void *ret;
  30.         //等待新线程结束,获得新线程的返回值
  31.     pthread_join(pid, &ret);
  32.     //打印线程返回值,这里强转为long long int是因为我的Linux是64位
  33.     //指针是八字节大小,long long int是八字节
  34.     cout << "main thread quit..., Thread 1 return val: " << (long long int)ret << endl;
  35.     return 0;
  36. }
复制代码

这给我们证明白,新线程的输出型参数是可以被主线程取到的,并且全局变量是可以被全部线程访问的,是共享资源,所以全局函数也是可以被全部线程访问的
&ret继承退出状态的详细过程
当调用 pthread_join 时,pthread_join 会阻塞当火线程,直到由 thread 参数指定的目标线程终止,一旦目标线程终止,pthread_join 会将该线程调用 pthread_exit 时通报的 void* 指针(即退出状态)赋值给 &ret 所指向的 void* 变量,即ret,pthread_join 乐成完成等待和状态获取后,会返回 0,表现利用乐成,当火线程可以继承实验后续代码
二、线程的局部存储

全局变量是被全部线程共享的,如果我们的线程必要有本身的私有的东西,也就是只能够本身访问,其他线程不能访问的,我们可以在全局变量前加关键字__thread修饰,这是编译器为我们提供的只能用来修饰内置范例的关键字
  1. #include <iostream>
  2. #include <pthread.h>
  3. #include <vector>
  4. #include <string>
  5. #include <unistd.h>
  6. using namespace std;
  7. #define NUM 3
  8. int *p = nullptr;
  9. //线程局部存储
  10. __thread int val = 100;
  11. class ThreadInfo
  12. {
  13. public:
  14.     ThreadInfo(const string &threadname)
  15.     :threadname_(threadname)
  16.     {}
  17. public:
  18.     string threadname_;
  19. };
  20. string toHex(pthread_t tid)
  21. {
  22.     char buffer[64];
  23.     snprintf(buffer, sizeof(buffer), "%p", tid);
  24.     return buffer;
  25. }
  26. void *threadroutine(void *args)
  27. {
  28.     int i = 0;
  29.     ThreadInfo *ti = static_cast<ThreadInfo*>(args);
  30.     //线程循环,每次打印线程名称、线程ID、进程ID、被修饰变量val以及val地址
  31.     while(i < 10)
  32.     {
  33.         cout << ti->threadname_.c_str() << " is running, tid: " << toHex(pthread_self()) << ", pid: " << getpid()  << ", val: " << val << ", &val: " << &val << endl;
  34.         i++;
  35.         val++;
  36.         usleep(10000);
  37.     }
  38.         delete ti;
  39.     return nullptr;
  40. }
  41. int main()
  42. {
  43.     vector<pthread_t> tids;
  44.     for(int i = 0; i < NUM; i++)
  45.     {
  46.         pthread_t tid;
  47.         ThreadInfo *ti = new ThreadInfo("Thread-"+to_string(i));
  48.         pthread_create(&tid, nullptr, threadroutine, ti);
  49.         tids.push_back(tid);
  50.         usleep(1000);
  51.     }
  52.         //线程等待
  53.     for(auto tid:tids)
  54.     {
  55.         pthread_join(tid, nullptr);
  56.     }
  57.     return 0;
  58. }
复制代码

我们通过观察可以发现,在雷同线程的情况下,val的值是递增的,但对于差别的线程之间val值是没有关系的,所以我们就通过关键字__thread实现了线程的局部存储,这些属于每个线程的val的地点在线程的独立栈中
三、开端明白线程互斥

1、互斥的概念



  • 临界资源:多线程实验流共享的资源叫做临界资源
  • 临界区:每个线程内部,访问临界资源的代码
  • 互斥:任何时候,有且只有一个实验流进入临界区,访问临界资源(对临界资源起掩护作用)
  • 原子性:不会被任何调理机制打断的利用,是不可再分隔的动作,该利用只有两种状态,一是完成,二是未完成(早期化学中,原子是构成物质的最小的不可分割的单位,在如许的配景下提出的原子性)
在大部分情况下,线程利用的数据都是局部变量,变量的地点空间在线程栈空间内,这种情况,变量属于单个线程,其他线程无法得到这个变量,但有时间,许多变量都必要在线程下共享,如许的变量被叫做共享变量,可以通过数据的共享,完成线程之间的交互
2、必要互斥的缘故原由

在各个线程访问共享变量的时间,会出现多进程并发的利用,大概会带来一些题目
下面是一个经典的抢票题目,每个线程访问到共享资源的票数就给它减一,就相当于是抢走一张票
  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <vector>
  5. #include <unistd.h>
  6. #include <pthread.h>
  7. using namespace std;
  8. //四个线程一起抢票
  9. #define NUM 4
  10. class threadData
  11. {
  12. public:
  13.     threadData(int number)
  14.     {
  15.         threadname = "thread-" + to_string(number);
  16.     }
  17. public:
  18.     string threadname;
  19. };
  20. //一次放出的票数
  21. int tickets = 1000;
  22. void *getTicket(void *args)
  23. {
  24.     threadData *td = static_cast<threadData *>(args);
  25.     const char *name = td->threadname.c_str();
  26.     while (true)
  27.     {
  28.         if(tickets > 0)
  29.         {
  30.             usleep(1000);
  31.             //提示出是谁抢了票,以及抢到的票号
  32.             printf("who=%s, get a ticket: %d\n", name, tickets);
  33.             tickets--;
  34.         }
  35.         else
  36.             break;
  37.     }
  38.     printf("%s ... quit\n", name);
  39.     return nullptr;
  40. }
  41. int main()
  42. {
  43.     vector<pthread_t> tids;
  44.     vector<threadData *> thread_datas;
  45.     for (int i = 1; i <= NUM; i++)
  46.     {
  47.         pthread_t tid;
  48.         threadData *td = new threadData(i);
  49.         thread_datas.push_back(td);
  50.         //这里最后一个参数因为下标从0开始,而我们的i是从1开始的,所以i-1
  51.         pthread_create(&tid, nullptr, getTicket, thread_datas[i - 1]);
  52.         tids.push_back(tid);
  53.     }
  54.     for (auto thread : tids)
  55.     {
  56.         pthread_join(thread, nullptr);
  57.     }
  58.     for (auto td : thread_datas)
  59.     {
  60.         delete td;
  61.     }
  62.     return 0;
  63. }
复制代码
我们将形成的步伐实验两遍
第一遍:

第二遍:

我们发现,抢票怎么还能抢出第0票呢,甚至另有-1、-2票?而且竟然另有抢到一张票的情况,下面我们来详解一下
起首,如果我们只讨论一个线程,整个抢票的过程就是,ticket在内存中,线程读取ticket,然后线程把ticket变量放到CPU上,CPU举行--利用,然后再放回内存中,将原来的值覆盖
我们这么说,这个过程是不是变得很慢了呢,所以在我们读取ticket之后,其他线程也来读取了,末了我们实验一圈后,如果他们都是一起实验完的,那么原来1000的值就变成了999,他们都抢到了第1000张票,这就是重复抢到同一张票的缘故原由
出现负数也是这个缘故原由,只不外不是同一时间做出返回内存的举动,在CPU举行盘算的时间,要重新读取数据,如果开始时全部线程都ticket==1,判断这里就能过得去,然后一个线程拿到了末了一张票1,其他三个线程就拿到了“伪钞”0、-1、-2,这就是我们要举行进程互斥的缘故原由

本日分享就到这里啦~


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

本帖子中包含更多资源

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

×
回复

使用道具 举报

×
登录参与点评抽奖,加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表