外贸建英文网站的重要性广东深圳龙岗区天气
web/
2025/10/3 23:05:03/
文章来源:
外贸建英文网站的重要性,广东深圳龙岗区天气,如何做类似于淘宝的网站,全国企业信息公示系统查询 作者#xff1a;დ旧言~ 座右铭#xff1a;松树千年终是朽#xff0c;槿花一日自为荣。 目标#xff1a;理解【Linux】多线程——线程概念|进程VS线程|线程控制 毒鸡汤#xff1a;有些事情#xff0c;总是不明白#xff0c;所以我不会坚持。早安! 作者დ旧言~ 座右铭松树千年终是朽槿花一日自为荣。 目标理解【Linux】多线程——线程概念|进程VS线程|线程控制 毒鸡汤有些事情总是不明白所以我不会坚持。早安! 专栏选自Linux初阶 望小伙伴们点赞收藏✨加关注哟
前言
早期我们的计算机还只能单个进程运行这样的话每个进程就只能独立存在不可以进行每个进程交互在这个基础上我们的先人大佬看看能不能多个进程同时运行也就有了现在的多进程那对比单个进程多进程有什么优势呢值不值得我们学习呢咱们带上这两个问题来康康Linux下的多线程。
⭐主体
学习【Linux】多线程——线程概念|进程VS线程|线程控制咱们按照下面的图解 地址空间和页表
地址空间是进程能看到的资源窗口一个进程能看到代码区、共享区、内核区、堆栈区大部分的资源都是在地址空间上看到的。
页表决定进程真正拥有资源的情况当前进程认为自己有了4GB可是实际上用了多少由页表决定最终能用多少物理资源。
合理的对地址空间与页表进行资源划分我们就可以对一个进程所有的资源进行分类通过地址空间分为栈区、堆区…通过页表映射到不同的物理内存。 页表的映射
在32位平台下一共有2³²个地址也就意味着有2³²个地址需要被映射 地址空间一共有2³²个地址每个地址单位都是1字节而页表也得有2³²个条目每个地址都得经过页表映射都是页表的每个条目包括物理地址包括是否命中包括RWX权限包括U/K权限一个条目假设为6个字节样例数据所以光保存页表所需空间为24GB4GB约为40亿字节。 每一个表项中除了要有虚拟地址和与其映射的物理地址以外实际还需要有一些权限相关的信息如用户级页表和内核级页表实际就是通过权限进行区分的 每个应表项中存储一个物理地址和一个虚拟地址就需要8个字节考虑到还需要包含权限相关的各种信息这里每一个表项就按10个字节计算
这里一共有2³²个表项也就意味着存储这张页表需要用2³² * 10个字节也就是40GB而在32位平台下我们的内存可能一共就只有4GB也就是说我们根本无法存储这样的一张页表 二级页表
虚拟地址在被转化的过程中不是直接转化的而是拆分成了10 10 12以32位平台为例其页表的映射过程如下 选择虚拟地址的前10个比特位在页目录当中进行查找找到对应的页表。再选择虚拟地址的10个比特位在对应的页表当中进行查找找到物理内存中对应页框的起始地址。最后将虚拟地址中剩下的12个比特位作为偏移量从对应页框的起始地址处向后进行偏移找到物理内存中某一个对应的字节数据。 相关说明 物理内存实际是被划分成一个个4KB大小的页框的而磁盘上的程序也是被划分成一个个4KB大小的页帧的当内存和磁盘进行数据交换时也是以4KB大小为单位进行加载和保存的。4KB 2¹²个字节一个页框中有2¹²个字节而访问内存的基本大小是1字节因此一个页框中就有2¹²个地址于是就可以将剩下的12个比特位作为偏移量从页框的起始地址处开始向后进行偏移从而找到物理内存中某一个对应字节数据。 这实际上就是所谓的二级页表其中页目录项是一级页表页表项是二级页表 每一个表项还是按10字节计算页目录和页表的表项都是2¹º个因此一个表的大小就是2¹º也就是10个字节也就是10KB页目录有2¹º个表项也就意味着页表有2¹º个也就是说一级页表有1张二级页表有2¹º张总共算下来大概就是10MB内存消耗并不高因此Linux中实际就是这样映射的。 **注意**Linux中32位平台用的是二级页表64位平台用的是多级页表 进程基础概念 线程是什么
概念
在一个程序里的一个执行路线就叫做线程thread。更准确的定义是线程是“一个进程内部的控制序列 ”一切进程至少都有一个执行线程;线程在进程内部运行本质是在进程地址空间内运行在Linux系统中在CPU眼中看到的PCB都要比传统的进程更加轻量化透过进程虚拟地址空间可以看到进程的大部分资源将进程资源合理分配给每个执行流就形成了线程执行流不同平台的多线程底层实现策略不一样我们讨论Linux平台。
进程对应的模型进程的创建实际上伴随着其进程控制块task_struct、进程地址空间mm_struct以及页表的创建虚拟地址和物理地址就是通过页表建立映射的 进程内核数据结构代码和数据,每个进程都有自己独立的进程地址空间和独立的页表也就意味着所有进程在运行时本身就具有独立性我们在创建“进程”时只创建PCB并要求创建出来的PCB不在独立创建与父进程同享PCB那么创建的结果就是下面这样的 因为我们可以通过虚拟地址空间页表的方式对进程进行资源划分单个“进程”执行力度一定要比之前的进程要细。上图中每个线程都是当前进程里的一个执行流线程在进程内部运行线程在进程的地址空间内运行拥有该进程的一部分资源。 如何理解线程
每个进程都有自己独立的进程地址空间和独立的页表也就意味着所有进程在运行时本身就具有独立性。 在创建进程时它要创建PCB页表建立代码和数据的映射关系…所以创建一个进程的成本非常高。 如果创建进程时只创建task_struct并要求创建出来的task_struct和父task_struct共享进程地址空间和页表。 现在创建的进程不再给你独立分配地址空间和页表而是都指向同一块地址空间共享同一块页表。所以这四个task_struct看到的资源都是一样的后续可以通过某种方式把代码区拆分成4块让这四个task_struct执行不同的代码区域。上述的区域(数据区堆区栈区)也是类似处理方式。换言之后续创建的3个task_struct都各自有自己的一小份代码和数据把这样的一份task_struct称之为线程。其中每一个线程都是当前进程里面的一个执行流也就是常说的线程是进程内部的一个执行分支。 线程在进程内部运行本质就是线程在进程地址空间内运行也就是说曾经这个进程申请的所有资源几乎都是被所有线程共享的。 线程比进程更细是因为其执行的代码和数据更小了。线程的调度成本更低了是因为它将来在调度的时候核心数据结构(地址空间和页表)均不用切换了。 上述线程仅仅是在Linux下的实现不同平台对线程管理可能不一样。 如Windows有真正的有关多线程的数据结构。而Linux并没有真正的对线程创建对应的数据结构。Linux的线程是用进程PCB模拟的。所以Linux并不能直接提供线程相关的接口只能提供轻量级进程的接口。 在用户层实现了一套用户层多线程方案以库的方式提供给用户进行使用。 pthread线程库。 CPU视角下Linux下PCB 其他OS内的PCB。 Linux下的进程统一称之为轻量级进程。 线程优点
创建一个新线程的代价要比创建一个新进程小得多。与进程之间的切换相比线程之间的切换需要操作系统做的工作要少很多。线程占用的资源要比进程少很多。能充分利用多处理器的可并行数量。在等待慢速I/O操作结束的同时程序可执行其他的计算任务。计算密集型应用为了能在多处理器系统上运行将计算分解到多个线程中实现。I/O密集型应用为了提高性能将I/O操作重叠。线程可以同时等待不同的I/O操作。 线程缺点
性能损失 一个很少被外部事件阻塞的计算密集型线程往往无法与其他线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多那么可能会有较大的性能损失。这里的性能损失指的是增加了额外的同步和调度开销而可用的资源不变。 健壮性降低编写多线程需要更全面更深入的考虑在一个多线程程序里因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的换句话说线程之间是缺乏保护的。缺乏访问控制进程是访问控制的基本粒度在一个线程中调用某些OS函数会对整个进程造成影响。编程难度提高编写与调试一个多线程程序比单线程程序困难得多。 线程异常
线程一旦异常会导致整个进程整体退出 单个线程如果出现除零野指针问题导致线程崩溃进程也会随着崩溃。线程是进程的执行分支线程出异常就类似进程出异常进而触发信号机制终止进程进程终止该进程内的所有线程也就随即退出。 线程用途
合理的使用多线程能提高CPU密集型程序的执行效率合理的使用多线程能提高IO密集型程序的用户体验 如一边写代码一边下载开发工具就是多线程运行的一种表现 进程VS线程 进程和线程 进程是资源分配的基本单位 线程是调度的基本单位 线程ID一组寄存器栈errno信号屏蔽字调度优先级 为什么线程切换的成本更低 地址空间和页表不需要切换。CPU内部是有L1~L3 cache如果进程切换cache就立即失效新进程过来只能重新缓存。 进程和线程的资源共享
进程的多个线程共享同一地址空间因此Text Segment、Data Segment都是共享的 如果定义一个函数在各线程中都可以调用如果定义一个全局变量在各线程中都可以访问到。 除此之外各线程还共享以下进程资源和环境 文件描述符表。每种信号的处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数)。当前工作目录。用户id和组id。 补充说明
__thread int g_val 100; // 修饰全局变量让每一个线程各自拥有一个全局的变量 -- 线程的局部存储进程和线程的关系 关于进程线程的问题
如何看待之前学习的单进程
具有一个线程执行流的进程。
引入线程后如何重新理解之前的进程
红色方框框起来的内容将这个整体称作进程曾经理解的进程 内核数据结构 进程对应的代码和数据现在的进程从内核角度看承担分配系统资源的基本实体 一个进程内部一定存在多个执行流那么这些执行流在CPU角度有区别吗
没有任何区别CPU不关心当前是进程还是线程这样的概念只关心PCBCPU调度的时候照样以task_struct为单位来进行调度。 只是这里task_struct背后的代码和页表只是曾经的代码和页表的一小部分而已。所以CPU执行的只是一小块代码和数据但并不妨碍CPU执行其它执行流。所以就可以把原本串行的所有代码转变成并发或并行的让这些代码在同一时间点得以推进。
总结如下
以前CPU看到的所有的task_struct都是一个进程现在CPU看到的所有的task_struct都是一个执行流(线程)
线程控制 POSIX线程库
使用
与线程有关的函数构成了一个完整的系列绝大多数函数的名字都是以“pthread_”开头的要使用这些函数库要通过引入头文pthread.h链接这些线程函数库时要使用编译器命令的“-lpthread”选项
pthread线程库是应用层的原生线程库
我们说过在Linux没有真正意义上的线程无法直接提供创建线程的系统接口只能给我们提供创建轻量级进程的接口。但是在用户的角度上当我们想创建一个线程时会使用thread_create这样的接口而不是我们上面所使用vfork函数用户不能直接访问OS所以OS在用户和系统调用之间提供了编写好的用户级线程库这个库一般称为pthread库。任何Linux操作系统都必须默认携带这个库这个库称为原生线程库。原生的线程库本质上就是对轻量级进程的系统调用clone进行了封装pthread_create使用户层模拟实现了一套线程相关的接口。我们认为的线程实际在OS内部会被转化成我们所谓的轻量级进程。
错误检查:
传统的一些函数是成功返回0失败返回-1并且对全局变量errno赋值以指示错误。pthreads函数出错时不会设置全局变量errno而大部分其他POSIX函数会这样做。而是将错误代码通过返回值返回。pthreads同样也提供了线程内的errno变量以支持其它使用errno的代码。对于pthreads函数的错误建议通过返回值判定因为读取返回值要比读取线程内的errno变量的开销更小。 创建线程——pthread_create
pthread_create讲解
pthread_create:创建线程的函数
#include pthread.hint pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);thread:获取线程的ID该参数是一个输出型参数attr:用于设置创建线程的属性传入nullptr表示默认这个属性基本不管start_routine:函数地址表示线程启动后要执行的函数arg:传给线程例程的参数返回值成功返回0失败返回错误码
举个栗子
#include iostream
#include pthread.h
#include assert.h
#include unistd.h
using namespace std;
void * thread_routine(void *args)
{const char*name (const char*)args;while(true){cout这是新线程我正在运行!nameendl;sleep(1);}
}int main()
{pthread_t tid;int n pthread_create(tid,nullptr,thread_routine,(void*)thread one);assert(0n);(void)n;while(true){cout这是主线程我真正运行endl;sleep(1);}return 0;
}这里编译运行需要注意这个接口是库给我们提供的使用的接口如果不是语言上的接口或者操作系统上的接口如果是库提供的那在编译时是不通过的我们需要找到这个库。-L找到库在哪里-I找到头文件在哪里但是这个库已经在系统里安装好了除了告诉库和头文件在哪之外还需要知道链接哪一个库 此时我们用ps axj命令查看当前进程的信息时虽然此时该进程中有两个线程但是我们看到的进程只有一个因为这两个线程都是属于同一个进程的 而使用ps -aL指令就可以显示当前的轻量级进程了 其中LWPLight Weight Process表示的就是轻量级进程的ID可以看到显示的两个轻量级进程的PID是相同的因为它们是属于同一个进程的。每个轻量级进程都有唯一的LWP。
注意主线程的PID和LWP是一样的。不一样的就是新线程。所以CPU调度的时候是以LWP为标识符表示特定一个执行流。线程一旦被创建几乎所有的资源都是被所有线程共享的。所以线程之间想交互数据就容易了直接就能看到。
线程也一定要有自己私有的资源
线程被调度就要有独立的PCB属性私有。线程切换时正在运行需要进行上下文保存要有私有的上下文结构。每个进程都要独立的运行每个线程都要有自己独立的栈结构。
主线程创建一批新线程
我们让主线程一次性创建十个新线程并让创建的每一个新线程都去执行start_routine函数也就是说start_routine函数会被重复进入即该函数是会被重入的
#include iostream
#include pthread.h
#include assert.h
#include unistd.h
#include vector
using namespace std;class ThreadData
{
public:pthread_t tid;char namebuffer[64];};//创建一批新线程
void* start_routine(void* args)
{sleep(1);ThreadData *td static_castThreadData *(args);int cnt 10;while(cnt){coutnew thread create success,name: td-namebuffer cnt : cnt--endl;sleep(1);}delete td;return nullptr;
}
int main()
{vectorThreadData* threads;
#define NUM 10for(int i 0;iNUM;i){ThreadData *td new ThreadData();snprintf(td-namebuffer,sizeof(td-namebuffer),%s:%d,thread,i1);pthread_create(td-tid,nullptr,start_routine,td);threads.push_back(td);// sleep(1);}for(autoiter:threads){coutcreate thread: iter-namebuffer : iter-tid sucess endl;}while(true){coutnew thread create success,name: main threadendl;sleep(1);}return 0;
} 并且start_routine是可重入函数没有产生二义性没有因为一个线程去影响另一个线程。并且在函数内定义的变量都是局部变量具有临时性在多线程情况下也没有问题。这也说明了每一个线程都有自己独立的栈结构
获取线程ID——pthread_self
获取线程ID1.创建线程时通过输出型参数获得2.通过pthread_self接口函数获得
#include pthread.h
pthread_t pthread_self(void);我们可以打印出主线程打印出新线程的ID新线程打印自己的ID看是否相同结果是相同的
#include iostream
#include pthread.h
#include assert.h
#include unistd.h
#include vector
using namespace std;string changeId(const pthread_t thread_id)
{char tid[128];snprintf(tid,sizeof(tid),0x%x,thread_id);return tid;
}
void* start_routine(void*args)
{std::string threadname static_castconst char*(args);while(true){coutthreadname running ... changeId(pthread_self())endl;sleep(1);}
}
int main()
{pthread_t tid;pthread_create(tid,nullptr,start_routine,(void*)thread 1);coutmain thread running ... new thread id: changeId(tid)endl;pthread_join(tid,nullptr);return 0;
} 线程等待——pthread_join
概念
一个线程创建出来那就要如同进程一样也是需要被等待的。如果线程不等待对应的PCB没被释放也会造成类似僵尸进程的问题内存泄漏。所以线程也要被等待
获取新线程的退出信息回收新线程对应的PCB等内核资源防止内存泄漏。
可以不关心线程的退出信息。
pthread_join:等待线程的函数
#include pthread.h
int pthread_join(pthread_t thread, void **retval);参数thread:被等待线程的IDretval:线程退出时的退出码信息void** retval:输出型参数主要用来获取线程函数结束时返回的退出结果。之所以是void**,是因为如果想作为输出型结果返回因为线程函数的返回结果是void*,而要把结果带出去就必须是void**,返回值线程等待成功返回0失败返回错误码
举个栗子
#include iostream
#include pthread.h
#include assert.h
#include unistd.h
#include vector
using namespace std;class ThreadData
{
public:int number;pthread_t tid;char namebuffer[64];};
class ThreadReturn
{
public:int exit_code;int exit_result;
};
//创建一批新线程
void* start_routine(void* args)
{ThreadData *td static_castThreadData *(args);int cnt 10;while(cnt){coutcnt:cntcnt:cntendl;cnt--;sleep(1);}ThreadReturn* tr new ThreadReturn();tr-exit_code 1;//线程退出码tr-exit_result 100;//线程退出结果return (void*)tr;//return (void*)td-number;//waring void*ret (void*)td-number;8字节、4字节
}
int main()
{vectorThreadData* threads;
#define NUM 10for(int i 0;iNUM;i){ThreadData *td new ThreadData();td-number i1;snprintf(td-namebuffer,sizeof(td-namebuffer),%s:%d,thread,i1);pthread_create(td-tid,nullptr,start_routine,td);threads.push_back(td);}for(autoiter:threads){coutcreate thread: iter-namebuffer : iter-tid sucess endl;}for(autoiter:threads){ThreadReturn*ret nullptr;int n pthread_join(iter-tid,(void**)ret);assert(n0);coutjoin : iter-namebuffer success,exit_code: ret-exit_code,exit_result: ret-exit_resultendl;delete iter;}coutmain thread quitendl;return 0;
}总结
没有看到线程退出时对应的退出信号这是因为线程出异常收到信号整个进程都会退出所以退出信号要由进程来关心所以pthread_join默认会认为函数会调用成功不考虑异常问题异常问题是进程该考虑的问题。 线程终止——return、pthread_exit、pthread_cancel
一个新创建出来的线程如果想终止线程而不是整个进程有三种做法:
直接从线程函数结束return的时候线程就算终止了。线程可以自己调用pthread_exit函数终止自己。一个线程可以调用pthread_cancel函数终止同一进程中的另一个线程。
return终止线程
注意:exit不能用来终止线程因为exit是来终止进程的。任何一个执行流调用exit都会让整个进程退出所以终止线程不能采用exit而是采用return来终止线程。
#include iostream
#include pthread.h
#include assert.h
#include unistd.h
#include vector
using namespace std;class ThreadData
{
public:pthread_t tid;char namebuffer[64];};
//创建一批新线程
void* start_routine(void* args)
{sleep(1);ThreadData *td static_castThreadData *(args);int cnt 10;while(cnt){coutcnt:cntcnt:cntendl;cnt--;sleep(1);return nullptr;}delete td;
}int main()
{vectorThreadData* threads;
#define NUM 10for(int i 0;iNUM;i){ThreadData *td new ThreadData();snprintf(td-namebuffer,sizeof(td-namebuffer),%s:%d,thread,i1);pthread_create(td-tid,nullptr,start_routine,td);threads.push_back(td);}for(autoiter:threads){coutcreate thread: iter-namebuffer : iter-tid sucess endl;}while(true){coutnew thread create success,name: main threadendl;sleep(1);}return 0;
}#include iostream
#include pthread.h
#include assert.h
#include unistd.h
#include vector
using namespace std;class ThreadData
{
public:pthread_t tid;char namebuffer[64];};
//创建一批新线程
void* start_routine(void* args)
{sleep(1);ThreadData *td static_castThreadData *(args);int cnt 10;while(cnt){coutcnt:cntcnt:cntendl;cnt--;sleep(1);return nullptr;}delete td;
}int main()
{vectorThreadData* threads;
#define NUM 10for(int i 0;iNUM;i){ThreadData *td new ThreadData();snprintf(td-namebuffer,sizeof(td-namebuffer),%s:%d,thread,i1);pthread_create(td-tid,nullptr,start_routine,td);threads.push_back(td);}for(autoiter:threads){coutcreate thread: iter-namebuffer : iter-tid sucess endl;}while(true){coutnew thread create success,name: main threadendl;sleep(1);}return 0;
} 最终新建线程终止。
pthread_exit函数 pthread_exit函数的功能就是终止线程 #include pthread.h
void pthread_exit(void *retval);retval:线程退出时的退出码信息默认设置为nullptr 举个栗子 #include iostream
#include pthread.h
#include assert.h
#include unistd.h
#include vector
using namespace std;class ThreadData
{
public:pthread_t tid;char namebuffer[64];};
//创建一批新线程
void* start_routine(void* args)
{sleep(1);ThreadData *td static_castThreadData *(args);int cnt 10;while(cnt){coutcnt:cntcnt:cntendl;cnt--;sleep(1);}delete td;pthread_exit(nullptr);
}
int main()
{vectorThreadData* threads;
#define NUM 10for(int i 0;iNUM;i){ThreadData *td new ThreadData();snprintf(td-namebuffer,sizeof(td-namebuffer),%s:%d,thread,i1);pthread_create(td-tid,nullptr,start_routine,td);threads.push_back(td);}for(autoiter:threads){coutcreate thread: iter-namebuffer : iter-tid sucess endl;}while(true){coutnew thread create success,name: main threadendl;sleep(1);}return 0;
} pthread_cancel 线程是可以被其他线程取消的但是线程要被取消前提是这个线程是已经运行起来了。pthread_create取消也是线程终止的一种 #include pthread.h
int pthread_cancel(pthread_t thread);我们以取消一半的线程为例 #include iostream
#include pthread.h
#include assert.h
#include unistd.h
#include vector
using namespace std;class ThreadData
{
public:int number;pthread_t tid;char namebuffer[64];
};
//创建一批新线程
void* start_routine(void* args)
{ThreadData *td static_castThreadData *(args);int cnt 10;while(cnt){coutcnt:cntcnt:cntendl;cnt--;sleep(1);}return (void*)100;
}
int main()
{vectorThreadData* threads;
#define NUM 10for(int i 0;iNUM;i){ThreadData *td new ThreadData();td-number i1;snprintf(td-namebuffer,sizeof(td-namebuffer),%s:%d,thread,i1);pthread_create(td-tid,nullptr,start_routine,td);threads.push_back(td);}for(autoiter:threads){coutcreate thread: iter-namebuffer : iter-tid sucess endl;}sleep(5);//取消一半的线程for(int i 0;ithreads.size()/2;i){pthread_cancel(threads[i]-tid);coutptheread_cancel : threads[i]-namebuffer successendl;}for(autoiter:threads){void*ret nullptr;int n pthread_join(iter-tid,(void**)ret);assert(n0);coutjoin : iter-namebuffer success,exit_code: (long long)retendl;delete iter;}coutmain thread quitendl;return 0;
} 线程如果是被取消的退出码是-1-1是一个宏PTHREAD_CANCELED我们可以查看定义
#define PTHREAD_CANCELED ((void *) -1)初步重新认识我们的线程库语言版
任何语言在Linux中如果要实现多线程必定要使用pthread库如何看待C11中的多线程C11的多线程在Linux环境中本质就是对pthread库的封装。 分离线程——pthread_detach
概念
线程是可以等待的等待的时候是join的等待的阻塞式等待。而如果线程我们不想等待不要等待该去进行分离线程处理。默认情况下新创建的线程是joinable的线程退出后需要对其进行pthread_join操作否则无法释放资源从而造成内存泄漏而如果我们不关心线程的返回值join是一种负担这个时候我们可以告诉OS当线程退出时自动释放线程资源这种策略就是线程分离。
phread_detach使用
#include pthread.h
int pthread_detach(pthread_t thread);下面我们创建新线程让主线程与新线程运行起来主线程等待新线程退出等待完毕返回n而现在让创建的新线程进行分离按照我们的预料此时应该是等待失败
#include iostream
#include pthread.h
#include assert.h
#include unistd.h
#include string.h
#include vector
using namespace std;string changeId(const pthread_t thread_id)
{char tid[128];snprintf(tid,sizeof(tid),0x%x,thread_id);return tid;
}
void*start_routine(void*args)
{string threadname static_castconst char*(args);pthread_detach(pthread_self());//线程分离设置为分离状态int cnt 5;while(cnt--){coutthreadname running ... changeId(pthread_self())endl;sleep(1);}return nullptr;
}
int main()
{pthread_t tid;pthread_create(tid,nullptr,start_routine,(void*)thread 1);string main_id changeId(pthread_self());coutmain thread running... new thread id:changeId(tid)main thread id: main_idendl;//一个线程默认是joinable的设置了分离状态不能够进行等待了int n pthread_join(tid,nullptr);coutresult:n: strerror(n)endl;return 0;
} 结束语 今天内容就到这里啦时间过得很快大家沉下心来好好学习会有一定的收获的大家多多坚持嘻嘻成功路上注定孤独因为坚持的人不多。那请大家举起自己的小手给博主一键三连有你们的支持是我最大的动力回见。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/86460.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!