当前位置: 首页 > news >正文

医院网站开发百度文库wordpress做商城好吗

医院网站开发百度文库,wordpress做商城好吗,彩页设计印刷公司,福州思企互联网站建设公司怎么样文章目录 一、 线程概念1. 什么是线程2. 线程的优点3.线程的缺点4. 线程异常5. 线程用途 二、 Linux进程VS线程1. 进程和线程2. 进程和线程的地址空间3. 进程和线程的关系 三、Linux线程控制1. POSIX线程库2. 线程创建3. 线程ID及进程地址空间布局4. 线程终止5. 线程等待6. 线程… 文章目录 一、 线程概念1. 什么是线程2. 线程的优点3.线程的缺点4. 线程异常5. 线程用途 二、 Linux进程VS线程1. 进程和线程2. 进程和线程的地址空间3. 进程和线程的关系 三、Linux线程控制1. POSIX线程库2. 线程创建3. 线程ID及进程地址空间布局4. 线程终止5. 线程等待6. 线程分离 一、 线程概念 1. 什么是线程 在Linux中一个进程的创建意味着进程控制块PCBtask_struct进程地址空间mm_struct和页表的建立。虚拟内存和物理内存之间的映射就是靠页表来完成的。 也就是说一个每一个进程都包含了独立的进程控制块PCBtask_struct进程地址空间mm_struct和页表这也是进程之间具有独立性的原因。 但我们多创建几个进程控制块task_struct但让他们共享统一个进程地址空间和页表如下图 起始这本质上就是创建了4个线程 我们说每一个线程是当先进程的一个执行流也就是常说的线程是进程内部的一个执行分支同时每个线程都是在进程内部运行的其本质上就是在进程地址空间内运行的就是说这个进程以前申请的所有资源都是被所有线程共享的。 值得注意的是进程不是有一个进程控制块task_struct就是一个进程进程控制块进程地址空间页表文件信号等等这些合起来叫一个进程。 而我们之前接触到的进程都只有一个task_struct也就是该进程内部只有一个执行流就是只有一个进程控制块即单执行流进程反之内部有多个执行流的进程叫做多执行流进程 在内核角度来看进程进程是承担系统分配资源的实体 而线程是cpu调度的基本单位。 那么在cpu内部能区分自己调度的task_struct是线程还是进程吗 答案是当然不行也没必要因为cpu只关心一个一个的执行流无论是单执行流还是多执行流cpu才不会管呢他就负责执行才不会管你是啥。 多执行流时cpu调度 单执行流时线程调度 在一个系统中存在大量的进程而一个进程中又存在大量线程那么系统中肯定存在着大量的线程那这么多线程需不需要管理呢当然是需要的那这么管理呢当然是六字真言先描述再组织。先把描述线程的变量描述在一个结构体当中然后再利用某种数据结构比如链表将一个个的结构体组织起来。这么一来对线程的增加删除就变成了对链表的增删查改。 但在Linux中是没有真正意义上的线程的因为Linux没有专门设计线程的管理因为线程和进程结构上比较类似所以对进程的管理方法进行了复用因此我们称Linux中的线程为轻量化的进程。 而在Windows中是存在真正的线程的因此Windows当中对于线程管理的设计一定比Linux当中的更复杂。 既然在Linux没有真正意义的线程那么也就绝对没有真正意义上的线程相关的系统调用 既然在Linux中都没有真正意义上的线程了那么自然也没有真正意义上的线程相关的系统调用了。但是Linux可以提供创建轻量级进程的接口也就是创建进程共享空间其中最典型的代表就是vfork函数。 vfork函数的功能就是创建子进程但是父子共享空间v函数fork的函数原型如下 pid_t vfork(void);vfork函数的返回值与fork函数的返回值相同 给父进程返回子进程的PID。给子进程返回0。 只不过vfork函数创建出来的子进程与其父进程共享地址空间符合线程的定义。 例如在下面的代码中父进程使用vfork函数创建子进程子进程将全局变量g_val由100改为了200父进程休眠3秒后再读取到全局变量g_val的值。 #include stdio.h #include stdlib.h #include sys/types.h #include unistd.h int g_val 100; int main() {pid_t id vfork();if (id 0){//childg_val 200;printf(child:PID:%d, PPID:%d, g_val:%d\n, getpid(), getppid(), g_val);exit(0);}//fathersleep(3);printf(father:PID:%d, PPID:%d, g_val:%d\n, getpid(), getppid(), g_val);return 0; } 可以看到父进程读取到g_val的值是子进程修改后的值也就证明了vfork创建的子进程与其父进程是共享地址空间的。 但在我们想要创建一个线程的时候更常用的是pthread_create这样的原生线程库封装的函数而不是vfork。 原生线程库实际就是对轻量级进程的系统调用进行了封装在用户层模拟实现了一套线程相关的接口。 因此对于我们来讲在Linux下学习线程实际上就是学习在用户层模拟实现的这一套接口而并非操作系统的接口。 总结 在一个程序里的一个执行路线就叫做线程thread。更准确的定义是线程是“一个进程内部的控制序列”一切进程至少都有一个执行线程线程在进程内部运行本质是在进程地址空间内运行在Linux系统中在CPU眼中看到的PCB都要比传统的进程更加轻量化透过进程虚拟地址空间可以看到进程的大部分资源将进程资源合理分配给每个执行流就形成了线程执行流 2. 线程的优点 创建一个新线程的代价要比创建一个新进程小得多与进程之间的切换相比线程之间的切换需要操作系统做的工作要少很多线程占用的资源要比进程少很多能充分利用多处理器的可并行数量在等待慢速I/O操作结束的同时程序可执行其他的计算任务计算密集型应用为了能在多处理器系统上运行将计算分解到多个线程中实现I/O密集型应用为了提高性能将I/O操作重叠。线程可以同时等待不同的I/O操作。 3.线程的缺点 性能损失 一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多那么可能会有较大的性能损失这里的性能损失指的是增加了额外的同步和调度开销而可用的资源不变。 健壮性降低 编写多线程需要更全面更深入的考虑在一个多线程程序里因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的换句话说线程之间是缺乏保护的。 缺乏访问控制 进程是访问控制的基本粒度在一个线程中调用某些OS函数会对整个进程造成影响。 编程难度提高 编写与调试一个多线程程序比单线程程序困难得多 4. 线程异常 单个线程如果出现除零野指针问题导致线程崩溃进程也会随着崩溃线程是进程的执行分支线程出异常就类似进程出异常进而触发信号机制终止进程进程终止该进程内的所有线程也就随即退出 5. 线程用途 合理的使用多线程能提高CPU密集型程序的执行效率合理的使用多线程能提高IO密集型程序的用户体验如生活中我们一边写代码一边下载开发工具就是多线程运行的一种表现 二、 Linux进程VS线程 1. 进程和线程 进程是资源分配的基本单位线程是调度的基本单位线程共享进程数据但也拥有自己的一部分数据: 线程ID一组寄存器栈errno信号屏蔽字调度优先级 2. 进程和线程的地址空间 进程的多个线程共享 同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境: 文件描述符表每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)当前工作目录用户id和组id 3. 进程和线程的关系 进程和线程的关系如下图: 三、Linux线程控制 1. POSIX线程库 pthread线程库是应用层的原生线程库 应用层指的是这个线程库并不是系统接口直接提供的而是由第三方帮我们提供的。原生指的是大部分Linux系统都会默认带上该线程库。与线程有关的函数构成了一个完整的系列绝大多数函数的名字都是以“pthread_”打头的。要使用这些函数库要通过引入头文件pthreaad.h。链接这些线程函数库时要使用编译器命令的“-lpthread”选项。 错误检查 传统的一些函数是成功返回0失败返回-1并且对全局变量errno赋值以指示错误。pthreads函数出错时不会设置全局变量errno而大部分POSIX函数会这样做而是将错误代码通过返回值返回。pthreads同样也提供了线程内的errno变量以支持其他使用errno的代码。对于pthreads函数的错误建议通过返回值来判定因为读取返回值要比读取线程内的errno变量的开销更小。 2. 线程创建 线程创建需要调用pthread_create函数这是函数原型 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);参数说明 thread获取创建成功的线程ID该参数是一个输出型参数。attr用于设置创建线程的属性一般传入NULL表示使用默认属性。start_routine该参数是一个函数地址表示线程例程即线程启动后要执行的函数。arg传给线程例程的参数。 如果创建线程成功则返回0失败返回错误码 当一个程序启动时就有一个进程被创建同时也有一个线程开始运行我们把这个线程叫做主线程 主线程的作用 在主线程中创建其他的线程在主线程中完成各种善后工作比如线程等待等等。 从函数原型中可以看出第三个参数是一个函数指针并且这个函数只有一个参数那就是void* 返回值也必须是void* 。当线程创建好之后这个线程就会执行该函数中的代码即新的执行流。 最后一个参数是传给线程所执行函数的参数类型也必须为void*类型所以传参的时候要注意强制类型转换。 下面我们让主线程调用pthread_create函数创建一个新线程此后新线程就会跑去执行自己的新例程而主线程则继续执行后续代码。 #include stdio.h #include pthread.h //需要包含的头文件 #include unistd.hvoid* Routine(void* arg) {char* msg (char*)arg;while (1){printf(I am %s\n, msg);sleep(1);} } int main() {pthread_t tid;//第一个参数输出型参数tid就是线程idpthread_create(tid, NULL, Routine, (void*)thread 1);while (1){printf(I am main thread!\n);sleep(2);}return 0; } 可以看到每隔两秒主线程输出一次每隔一秒子线程输出一次 利用ps -axj命令查看进程我们发现只有一个进程和预想中的符合因为本来就是一个进程然后进程内有两个线程 我们可以使用ps -aL命令查看一下当前的线程就几个 默认情况下不带-L看到的就是一个个的进程。带-L就可以查看到每个进程内的多个轻量级进程。 可以看到有两个ceshi他们的PID一样也就是进程ID一样但是LWP不一样那LWP是啥呢LWP起始就是Lightweight process轻量化进程的意思也就是线程ID可以看到两个ceshi的线程ID是不一样的说明这是两个线程。其中一个线程的LWP和PID一样说明他是主线程。 我们之前接触到的都是单线程进程其PID和LWP是相等的所以对于单线程进程来说调度时采用PID和LWP是一样的。 为了进一步说明这两线程属于同一个进程我们可以让两个线程把他们的PID和PPID都打印出来。 #include stdio.h #include pthread.h #include unistd.h #include sys/types.hvoid* Routine(void* arg) {char* msg (char*)arg;while (1){printf(I am %s...pid: %d, ppid: %d\n, msg, getpid(), getppid());sleep(1);} } int main() {pthread_t tid;pthread_create(tid, NULL, Routine, (void*)thread 1);while (1){printf(I am main thread...pid: %d, ppid: %d\n, getpid(), getppid());sleep(2);}return 0; } 可以看到主线程和新线程的PID和PPID是一样的也就是说主线程和新线程虽然是两个执行流但它们仍然属于同一个进程。 当然我们也可以利用循环直接创建一批线程然后让新线程都去执行同一个函数此时这个函数会被重复执行我们称这个函数是重入的。 #include stdio.h #include stdlib.h #include pthread.h #include unistd.h #include sys/types.hvoid* Routine(void* arg) {char* msg (char*)arg;while (1){printf(I am %s...pid: %d, ppid: %d\n, msg, getpid(), getppid());sleep(1);} } int main() {pthread_t tid[5];for (int i 0; i 5; i){char* buffer (char*)malloc(64);sprintf(buffer, thread %d, i);pthread_create(tid[i], NULL, Routine, buffer);}while (1){printf(I am main thread...pid: %d, ppid: %d\n, getpid(), getppid());sleep(2);}return 0; } 可以看出同时运行了六个线程并且这六个线程PID一样属于同一个进程。 3. 线程ID及进程地址空间布局 线程id有两种获取方式 第一种是在线程建立时通过输出型参数pthread_t *thread来获得第二种是调用pthread_self 函数进行获取哪个线程调用这个函数这个函数就会返回哪个线程的线程id pthread_self的函数原型 pthread_t pthread_self(void);下面的代码展示了主线程中创建了五个子线程每次创建一个线程之后通过输出型参数输出所创建线程的ID然后在子线程中每个子线程通过调用pthread_self()函数输出自己的线程ID最后在主线程中调用pthread_self()输出自己的线程ID #include stdio.h #include stdlib.h #include pthread.h #include unistd.h #include sys/types.hvoid* Routine(void* arg) {char* msg (char*)arg;while (1){printf(I am %s...pid: %d, ppid: %d, tid: %lu\n, msg, getpid(), getppid(), pthread_self());//函数中通过调用pthread_self()输出子线程ID//哪个子线程调用它就输出哪个子线程的IDsleep(1);} } int main() {pthread_t tid[5];for (int i 0; i 5; i){char* buffer (char*)malloc(64);sprintf(buffer, thread %d, i);pthread_create(tid[i], NULL, Routine, buffer);printf(%s tid is %lu\n, buffer, tid[i]);//主函数中通过输出型参数输出以此线程ID}while (1){printf(I am main thread...pid: %d, ppid: %d, tid: %lu\n, getpid(), getppid(), pthread_self());//主线程调用pthread_self()函数输出主线程IDsleep(2);}return 0; } 可以看到两种方式获取的线程ID其实是一样的当然一样不一样就怪了。另外用pthread_self函数获得的线程ID与内核的LWP的值是不相等的pthread_self函数获得的是用户级原生线程库的线程ID而LWP是内核的轻量级进程ID它们之间是一对一的关系。 关于线程ID和进程地址空间的那些事 pthread_create函数会产生一个线程ID存放在第一个参数指向的地址中该线程ID和内核中的LWP不是一回事。内核中的LWP属于进程调度的范畴因为线程是轻量级进程是操作系统调度器的最小单位所以需要一个数值来唯一表示该线程。pthread_create函数第一个参数指向一个虚拟内存单元该内存单元的地址即为新创建线程的线程ID这个ID属于NPTL线程库的范畴线程库的后续操作就是根据该线程ID来操作线程的。线程库NPTL提供的pthread_self函数获取的线程ID和pthread_create函数第一个参数获取的线程ID是一样的。 Linux不提供真正的线程只提供LWP也就意味着操作系统只需要对内核执行流LWP进行管理而供用户使用的线程接口等其他数据应该由线程库自己来管理因此管理线程时的“先描述再组织”就应该在线程库里进行。 既然是在线程库里进行先描述在组织那线程库在哪里呢 同通过ldd指令我们可以看出线程库是一个动态库因此在进程建立的时候线程库会被加载到进程地址空间中的共享区。又因为线程之间进程地址空间是共享的所以所有线程都可以看到这个库。 每个线程都有自己私有的栈主线程采用的栈是进程地址空间中的栈而其余线程采用的栈就是在共享区中开辟的。每个线程都有自己的struct pthread当中包含了对应线程的各种属性每个线程还有自己的线程局部存储当中包含了对应线程被切换时的上下文数据。 这些东西都在共享区中存储因此我们只要知道数据在共享区中的地址就可以靠地址找到它们进行管理。因此我们说其实线程ID就是进程地址空间中的一个地址罢了。 上面我们所用的各种线程函数本质都是在库内部对线程属性进行的各种操作最后将要执行的代码交给对应的内核级LWP去执行就行了也就是说线程数据的管理本质是在共享区的。 pthread_t到底是什么类型取决于实现但是对于Linux目前实现的NPTL线程库来说线程ID本质就是进程地址空间共享区上的一个虚拟地址同一个进程中所有的虚拟地址都是不同的因此可以用它来唯一区分每一个线程。 例如我们也可以尝试按地址的形式对获取到的线程ID进行打印。 #include stdio.h #include pthread.h #include unistd.hvoid* Routine(void* arg) {while (1){printf(new thread tid: %p\n, pthread_self());sleep(1);} } int main() {pthread_t tid;pthread_create(tid, NULL, Routine, NULL);while (1){printf(main thread tid: %p\n, pthread_self());sleep(2);}return 0; } 可以看出线程ID本质上就是地址。 4. 线程终止 如果需要只终止某个线程而不终止整个进程,可以有三种方法: 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。线程可以调用pthread_ exit终止自己。一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。 return退出 #include iostream #include pthread.h #include sys/types.h #include unistd.hvoid *Routine(void *args) {char *msg (char *)args;int cnt 0;while (cnt 5){printf(I am %s...pid: %d, ppid: %d, tid: %lu\n, msg, getpid(), getppid(), pthread_self());sleep(1);cnt;}return (void *)2024; }int main() {pthread_t tid[5];for (int i 0; i 5; i){char *buffer (char *)malloc(64);sprintf(buffer, pthread %d, i);pthread_create(tid[i], nullptr, Routine, buffer);printf(%s tid is %lu\n, buffer, tid[i]);}return 0; }可以看到并没有执行子线程中的输出语句那是因为我们这里没有阻塞的进行线程等待而是直接执行完return语句主线程直接退出了主线程退出了进程直接就结束了自然其他线程也不会执行输出语句。 通过pthread_exit()函数进行终止 pthread_exit函数 功能线程终止 原型 void pthread_exit(void *value_ptr);参数 value_ptr:value_ptr不要指向一个局部变量。返回值无返回值跟进程一样线程结束的时候无法返回到它的调用者自身需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。 此处我们创建五个线程输出5次之后利用pthread_exit函数返回8888 #include iostream #include pthread.h #include sys/types.h #include unistd.hvoid *Routine(void *args) {char *msg (char *)args;int cnt 0;while (cnt 5){printf(I am %s...pid: %d, ppid: %d, tid: %lu\n, msg, getpid(), getppid(), pthread_self());sleep(1);cnt;}pthread_exit((void *)8888); }int main() {pthread_t tid[5];for (int i 0; i 5; i){char *buffer (char *)malloc(64);sprintf(buffer, pthread %d, i);pthread_create(tid[i], nullptr, Routine, buffer);printf(%s tid is %lu\n, buffer, tid[i]);}printf(I am main thread...pid: %d, ppid: %d, tid: %lu\n, getpid(), getppid(), pthread_self());for (int i 0; i 5; i){void *ret nullptr;pthread_join(tid[i], ret);printf(thread %d[%lu]...quit, exitcode: %lld\n, i, tid[i], (long long int)ret);}return 0; }注意 exit函数的作用是终止进程任何一个线程调用exit函数也代表的是整个进程终止。 pthread_cancel(pthread_self())终止线程 pthread_cancel函数 功能 取消一个执行中的线程 原型 int pthread_cancel(pthread_t thread);参数 thread:线程ID返回值成功返回0失败返回错误码 此处我们创建5个线程在线程中调用pthread_cancel函数终止自己在主线程中输出退出码按我们上面说的退出码应该是-1。 #include iostream #include pthread.h #include sys/types.h #include unistd.hvoid *Routine(void *args) {char *msg (char *)args;int cnt 0;while (cnt 5){printf(I am %s...pid: %d, ppid: %d, tid: %lu\n, msg, getpid(), getppid(), pthread_self());sleep(1);cnt;pthread_cancel(pthread_self());}return (void *)2024;// pthread_exit((void *)6666); }int main() {pthread_t tid[5];for (int i 0; i 5; i){char *buffer (char *)malloc(64);sprintf(buffer, pthread %d, i);pthread_create(tid[i], nullptr, Routine, buffer);printf(%s tid is %lu\n, buffer, tid[i]);}printf(I am main thread...pid: %d, ppid: %d, tid: %lu\n, getpid(), getppid(), pthread_self());for (int i 0; i 5; i){void *ret nullptr;pthread_join(tid[i], ret);printf(thread %d[%lu]...quit, exitcode: %lld\n, i, tid[i], (long long int)ret);}return 0; }这个函数不仅可以自己取消自己也可以在其他线程中取消其他线程。比如我们在主线程中通过调用pthread_cancle()取消同一进程中的其他4个线程。 #include iostream #include pthread.h #include sys/types.h #include unistd.hvoid *Routine(void *args) {char *msg (char *)args;int cnt 0;while (cnt 5){printf(I am %s...pid: %d, ppid: %d, tid: %lu\n, msg, getpid(), getppid(), pthread_self());sleep(1);cnt;}pthread_exit((void *)8888); }int main() {pthread_t tid[5];for (int i 0; i 5; i){char *buffer (char *)malloc(64);sprintf(buffer, pthread %d, i);pthread_create(tid[i], nullptr, Routine, buffer);printf(%s tid is %lu\n, buffer, tid[i]);}printf(I am main thread...pid: %d, ppid: %d, tid: %lu\n, getpid(), getppid(), pthread_self());pthread_cancel(tid[0]);pthread_cancel(tid[1]);pthread_cancel(tid[2]);pthread_cancel(tid[3]);for (int i 0; i 5; i){void *ret nullptr;pthread_join(tid[i], ret);printf(thread %d[%lu]...quit, exitcode: %lld\n, i, tid[i], (long long int)ret);}return 0; }可以看到四个线程直接退出退出码是1最后剩余一个线程由于没有被终止因此输出五次之后正常退出退出码为8888. 5. 线程等待 为什么要线程等待 已经退出的线程其空间没有被释放仍然在进程的地址空间内。创建新的线程不会复用刚才退出线程的地址空间。 功能等待线程结束 原型 int pthread_join(pthread_t thread, void **value_ptr);参数 thread:线程IDvalue_ptr:它指向一个指针后者指向线程的返回值返回值成功返回0失败返回错误码 调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的总结如下: 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数 PTHREAD_ CANCELED。用grep命令进行查找可以发现PTHREAD_CANCELED实际上就是头文件pthread.h里面的一个宏定义它的值本质就是-1。如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参 数。如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数 #include iostream #include pthread.h #include sys/types.h #include unistd.hvoid *Routine(void *args) {char *msg (char *)args;int cnt 0;while (cnt 5){printf(I am %s...pid: %d, ppid: %d, tid: %lu\n, msg, getpid(), getppid(), pthread_self());sleep(1);cnt;}pthread_exit((void *)0); }int main() {pthread_t tid[5];for (int i 0; i 5; i){char *buffer (char *)malloc(64);sprintf(buffer, pthread %d, i);pthread_create(tid[i], nullptr, Routine, buffer);printf(%s tid is %lu\n, buffer, tid[i]);}printf(I am main thread...pid: %d, ppid: %d, tid: %lu\n, getpid(), getppid(), pthread_self());for (int i 0; i 5; i){pthread_join(tid[i], nullptr);printf(thread %d[%lu]...quit\n, i, tid[i]);}return 0; } 可以看出主线程成功对这五个线程进行了等待。 面我们再来看看如何获取线程退出时的退出码为了便于查看我们这里将线程退出时的退出码设置为某个特殊的值比如2024并在成功等待线程后将该线程的退出码进行输出。 注意输出时要强转成long long int #include iostream #include pthread.h #include sys/types.h #include unistd.hvoid *Routine(void *args) {char *msg (char *)args;int cnt 0;while (cnt 5){printf(I am %s...pid: %d, ppid: %d, tid: %lu\n, msg, getpid(), getppid(), pthread_self());sleep(1);cnt;}return (void *)2024; }int main() {pthread_t tid[5];for (int i 0; i 5; i){char *buffer (char *)malloc(64);sprintf(buffer, pthread %d, i);pthread_create(tid[i], nullptr, Routine, buffer);printf(%s tid is %lu\n, buffer, tid[i]);}printf(I am main thread...pid: %d, ppid: %d, tid: %lu\n, getpid(), getppid(), pthread_self());for (int i 0; i 5; i){void *ret nullptr;pthread_join(tid[i], ret);printf(thread %d[%lu]...quit, exitcode: %lld\n, i, tid[i], (long long int)ret);}return 0; } 可以看到线程等待成功并且返回值为2024. 注意 pthread_join函数默认是以阻塞的方式进行线程等待的。 为什么线程退出时只能拿到线程的退出码 如果我们等待的是一个进程那么当这个进程退出时我们可以通过wait函数或是waitpid函数的输出型参数status获取到退出进程的退出码、退出信号以及core dump标志。 那为什么等待线程时我们只能拿到退出线程的退出码难道线程不会出现异常吗 线程在运行过程中当然也会出现异常线程和进程一样线程退出的情况也有三种 代码运行完毕结果正确。代码运行完毕结果不正确。代码异常终止。 因此我们也需要考虑线程异常终止的情况但是pthread_join函数无法获取到线程异常退出时的信息。因为线程是进程内的一个执行分支如果进程中的某个线程崩溃了那么整个进程也会因此而崩溃此时我们根本没办法执行pthread_join函数因为整个进程已经退出了。 例如我们在线程的执行例程当中制造一个除零错误当某一个线程执行到此处时就会崩溃进而导致整个进程崩溃。 #include stdio.h #include stdlib.h #include pthread.h #include unistd.h #include sys/types.hvoid* Routine(void* arg) {char* msg (char*)arg;int count 0;while (count 5){printf(I am %s...pid: %d, ppid: %d, tid: %lu\n, msg, getpid(), getppid(), pthread_self());sleep(1);count;int a 1 / 0; //error}return (void*)2022; } int main() {pthread_t tid[5];for (int i 0; i 5; i){char* buffer (char*)malloc(64);sprintf(buffer, thread %d, i);pthread_create(tid[i], NULL, Routine, buffer);printf(%s tid is %lu\n, buffer, tid[i]);}printf(I am main thread...pid: %d, ppid: %d, tid: %lu\n, getpid(), getppid(), pthread_self());for (int i 0; i 5; i){void* ret NULL;pthread_join(tid[i], ret);printf(thread %d[%lu]...quit, exitcode: %d\n, i, tid[i], (int)ret);}return 0; } **一个线程挂了全部线程就都挂了所以我们也不知道到底是哪个线程出了问题可见多线程健壮性不强。 所以pthread_join函数只能获取到线程正常退出时的退出码用于判断线程的运行结果是否正确。 6. 线程分离 默认情况下新创建的线程是joinable的线程退出后需要对其进行pthread_join操作否则无法释放资源从而造成系统泄漏。如果不关心线程的返回值join是一种负担这个时候我们可以告诉系统当线程退出时自动释放线程资源。一个线程如果被分离了这个线程依旧要使用该进程的资源依旧在该进程内运行甚至这个线程崩溃了一定会影响其他线程只不过这个线程退出时不再需要主线程去join了当这个线程退出时系统会自动回收该线程所对应的资源。可以是线程组内其他线程对目标线程进行分离也可以是线程自己分离:joinable和分离是冲突的一个线程不能既是joinable又是分离的。 int pthread_detach(pthread_t thread); pthread_detach(pthread_self());参数说明 thread被分离线程的ID。 返回值说明 线程分离成功返回0失败返回错误码。 我们可以在线程中调用这个函数这样主线程中就不需要进行线程等待子线程运行结束之后系统会自动回收资源。 值得注意的是虽然主线程不需要等待了但还是需要让主线程最后退出如果主线程提前退出了相当于进程直接结束了那其他线程也就直接结束了。 #include iostream #include pthread.h #include sys/types.h #include unistd.hvoid *Routine(void *args) {pthread_detach(pthread_self());//线程调用该函数分离自己char *msg (char *)args;int cnt 0;while (cnt 5){printf(I am %s...pid: %d, ppid: %d, tid: %lu\n, msg, getpid(), getppid(), pthread_self());sleep(1);cnt;}// return (void *)2024;pthread_exit((void *)6666); }int main() {pthread_t tid[5];for (int i 0; i 5; i){char *buffer (char *)malloc(64);sprintf(buffer, pthread %d, i);pthread_create(tid[i], nullptr, Routine, buffer);printf(%s tid is %lu\n, buffer, tid[i]);}while (1){printf(I am main thread...pid: %d, ppid: %d, tid: %lu\n, getpid(), getppid(), pthread_self());sleep(1);}return 0; }
http://www.laogonggong.com/news/129013.html

相关文章:

  • 网站建设的业务范围保定网站设计制作公司
  • 适合大学生创业的网站建设类型模板建站配云服务器施工
  • 百度怎么不收录我的网站城市分站网站设计
  • 网站建设网络推广方案ppt优化排名seo
  • 怎样用ps做网站线上如何做推广
  • 渠道合作一站式平台dede小说网站模板
  • 数据库与网站重庆网站建设与制作
  • 社保个人网站入口建站快车代理平台系统
  • 视频网站直播如何做网站不用了 怎么关闭吗
  • 英文网站cms购买完域名后怎么做网站
  • wordpress自动粘贴图片网站怎么做区域性优化
  • 网站内容建设的原则是什么wordpress 上一页下一页
  • 如何查询网站打开速度怎么做租房网站
  • 嘉兴装修公司做网站吴中区做网站的公司
  • 沈阳网站建设方案策划页面模板怎么删除
  • 广州网站建设联系信科海珠在线制作个人网站
  • 网站建设模范代码wordpress移动主题设计
  • 南昌网站建设方案宁波seo免费优化软件
  • 开封府景点网站及移动端建设情况威海高区有没有建设局的网站
  • 合肥网站建设哪家专业科技微网站
  • 什么平台可以做网站推广怎么给wordpress切图
  • wordpress 和帝国郑州seo技术服务顾问
  • 做网站用什么编程语言好软件ui设计软件
  • 网站开发的核心技术网站引流.
  • 连云港品牌网站建设网站建设互联网营销营销推广
  • 开源网站搭建国内做服装的网站有哪些
  • 佛山市城乡住房建设局网站WordPress插件引入
  • 市场部做网站工作职责继续浏览此网站(不推荐)
  • 马卡龙网站建设方案现在石家庄做网站的公司有哪几家
  • 钓鱼网站免费空间服装网站技术解决方案