【Linux多线程】线程的终止、等待和分离

embedded/2024/10/21 11:33:35/

文章目录

  • 线程终止
    • 正常退出
      • return 退出
      • pthread_exit函数终止线程
    • pthread_cancel强制终止线程
    • 进程终止
  • 线程等待
    • 为什么需要等待线程?
    • pthread_join函数
  • 分离线程
    • pthread_detach函数

线程终止

下面给出终止线程的三种方式:

  1. 正常退出
    • 线程执行完它的函数之后return自动结束
    • 线程显示调用pthread_exit函数退出
  2. 强制终止
    • 一个线程可以被另一个线程通过pthread_cancel函数强制退出
  3. 进程终止
    • 一个进程终止,那么该进程的所有线程都会终止

下面将分别模拟这三种终止线程的方式:

正常退出

return 退出

给出下面代码模拟线程return终止

#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;void *rout(void *arg)
{int cnt = 3;while (cnt--){cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;sleep(1);}cout << "phread end....." << endl;sleep(1);return NULL;
}int main()
{pthread_t tid;int num = 10;int res = pthread_create(&tid, NULL, rout, (void *)(&num));pthread_join(tid, NULL); // 等待线程结束sleep(20);cout << "wait success! main thread end!" << endl;return 0;
}

在这里插入图片描述
观察上面代码运行情况,发现当线程return之后确实被终止了(上图多出来一个线程是库里面的管理线程,不必理会)。

pthread_exit函数终止线程

pthread-exit函数用于终止当前线程
函数原型:

void pthread_exit(void *value_ptr);
  • value_ptr是一个任意类型的指针,表示线程的退出状态。其它线程可以通过pthread_join函数获取这个状态

给出下面代码观察线程终止

#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;void *rout(void *arg)
{int cnt = 3;int *status = new int(20);while (true){cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;sleep(1);if (cnt == 0){cout << "phread exit....." << endl;pthread_exit((void *)(status));}cnt--;}// cout << "phread end....." << endl;// sleep(1);return NULL;
}int main()
{pthread_t tid;int num = 10;int res = pthread_create(&tid, NULL, rout, (void *)(&num));void *status = NULL;pthread_join(tid, &status); // 等待线程结束sleep(5);cout << "wait success! main thread end! status: " << *(int *)(status) << endl;return 0;
}

在这里插入图片描述
需要注意,pthread_exit或者return返回的指针指向的内存单元应该是全局的,因为线程终止之后其函数栈帧会销毁,之后才会返回一个void*指针。

pthread_cancel强制终止线程

功能:取消一个执行中的线程
函数原型:

int pthread_cancel(pthread_t thread);
  • thread表示要删除的线程id(用户级)
  • 成功返回0,否则返回错误码

给出代码样例,观察pthread_cancel函数的使用

#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;void *rout(void *arg)
{int cnt = 3;int *status = new int(20);while (true){cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;sleep(1);// if (cnt == 0)// {//     cout << "phread exit....." << endl;//     pthread_exit((void *)(status));// }// cnt--;}// cout << "phread end....." << endl;// sleep(1);return NULL;
}int main()
{pthread_t tid;int num = 10;int res = pthread_create(&tid, NULL, rout, (void *)(&num));void *status = NULL;sleep(5);pthread_cancel(tid); // 强制终止tidcout << "thread is end" << endl;pthread_join(tid, &status); // 等待线程结束sleep(3);cout << "wait success! main thread end! status: " << *(int *)(status) << endl;return 0;
}

在这里插入图片描述
在主线程中调用pthread_cancel函数强制终止了执行rout函数的线程。

进程终止

进程终止,该进程的所有线程都终止。比如在函数中调用exit终止进程。

void *rout(void *arg)
{int cnt = 3;int *status = new int(20);while (true){cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;sleep(1);if (cnt == 0){cout << "phread exit....." << endl;exit(0);}cnt--;}return NULL;
}

一旦调用exit或者异常而终止进程,该进程的所有线程都玩完。道理很简单,这里就不做演示了。

线程等待

为什么需要等待线程?

其实已经退出的线程并没有完全“结束”,其栈帧并没有随着线程终止马上就释放,仍然在进程的地址空间里。并且,如果不对这些已经终止但是还没有被释放空间的线程做处理,往后继续创建新线程都不会复用前面退出线程的地址空间,这就造成了资源的浪费。这种情况其实非常像我们之前谈过的僵尸进程问题。

等待线程终止就是提醒内核可以释放这个线程的资源了。等待线程终止其实也是为了确保线程完成任务。有时主线程或者其他线程需要等待某一个线程任务完成之后才能继续执行。等待一个线程结束,其实就是让终止的线程退出时“通知”一下其它线程,可以不关心退出线程的返回结果。此外,等待线程终止在某些情况下能保证数据的完整性,比如线程一处理上半段数据,线程二处理下半段数据,如果其中任何一个线程没有终止,那总数据就会不完整。

为了实现线程终止时的等待问题,linux提供了pthread_join函数。

pthread_join函数

作用:阻塞调用该函数线程,直到目标线程终止。
函数原型:

#include<pthread.h>
int pthread_join(pthread_t thread,void** retval);

在这里插入图片描述
其中:

  • thread表示所等待线程的线程标识符
  • retval是一个输出型参数,指向等待线程的退出信息
  • 成功返回0,否则返回错误码

值得注意的是,线程终止方式的不同,其通过pthread_join得到的退出信息也就不同。比如:

  1. 目标线程通过return 终止(或者是pthread_exit),retval所指向的单元里存放的就是目标线程执行函数的返回值。
    观察下面代码,分析线程return终止时,pthread_join得到的返回值
#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;void *rout(void *arg)
{int cnt = 3;int *status = new int(20);int num = 20;while (cnt--){cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;sleep(1);}// pthread_exit((void *)status);return (void *)status;
}int main()
{pthread_t tid;int num = 10;int res = pthread_create(&tid, NULL, rout, (void *)(&num));void *status = NULL;cout << "main pthread wait...." << endl;pthread_join(tid, &status); // 等待线程结束sleep(3);cout << "wait success! main thread end! status: " << *(int *)(status) << endl;return 0;
}

在这里插入图片描述

  1. 如果线程是被别的线程通过调用pthread_cancel函数强制终止掉,retval所指向单元存放的就是常数PTHREAD_ CANCELED

给出代码样例观察结果:

#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;void *rout(void *arg)
{int cnt = 3;int *status = new int(20);int num = 20;while (cnt--){cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;sleep(1);}pthread_exit((void *)status);// return (void *)status;
}int main()
{pthread_t tid;int num = 10;int res = pthread_create(&tid, NULL, rout, (void *)(&num));void *status = NULL;sleep(2);pthread_cancel(tid);pthread_join(tid, &status); // 等待线程结束if (status == PTHREAD_CANCELED){cout << "pthread is cancel" << endl;}return 0;
}

在这里插入图片描述
retval所指向单元存放的就是常数PTHREAD_ CANCELED得证。

  1. 如果不关心某一个线程的退出信息,可以return NULL。比如:
void* thread_function(void* arg) {// 线程的工作printf("Thread is running\n");return NULL;
}

总结:

分离线程

分离线程实际上是线程的一种状态,这种状态表示该进程不需要被等待,且线程退出后会自动释放资源。对于主线程来说,有些线程独立执行任务,其它线程没有必要再调用pthread_join阻塞等待。这样提升了整体的效率。一般来说,创建的新线程默认都是joinable的,也就是需要被等待的,我们可以通过pthread_detach函数来改变这一性质。

pthread_detach函数

功能:分离一个目标线程,使该线程终止后自动释放资源,不需要被等待
函数原型:

#include<pthread.h>
int pthread_detach(pthread_t thread);
  • thread表示分离的目标线程,也可以是调用该函数的线程本身
  • 成功放回0,否则-1

给出代码样例,观察pthread_detach函数的使用

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
using namespace std;void *thread_run(void *arg)
{pthread_detach(pthread_self());printf("%s\n", (char *)arg);return NULL;
}int main(void)
{pthread_t tid;if (pthread_create(&tid, NULL, thread_run, (void *)"thread1 run...") != 0){printf("create thread error\n");return 1;}int ret = 0;sleep(1); // 很重要,要让线程先分离,再等待if (pthread_join(tid, NULL) == 0){printf("pthread wait success\n");ret = 0;}else{printf("pthread wait failed\n");ret = 1;}return ret;
}

在这里插入图片描述
分析上述代码,因为执行thread_run函数的线程设置成了分离状态,函数结束之后自动释放资源。此时再去用pthread_join函数去等待这个线程就会得到返回值0。


http://www.ppmy.cn/embedded/46741.html

相关文章

HTML跳动的爱心

目录 写在前面 HTML简介 程序设计 修改文字 推荐系列 写在后面 写在前面 本期小编给大家分享可以写字的html动态爱心代码&#xff0c;一起来看看叭~ HTML简介 HTML&#xff08;HyperText Markup Language&#xff09;是一种用于创建网页的标记语言。它是互联网的基础&…

如何通过 6 种简单方法将照片从华为转移到 PC?

华为作为全球领先的智能手机供应商之一&#xff0c;最近推出了其自主研发的操作系统——HarmonyOS 2.0&#xff0c;旨在为智能手机、平板电脑和智能手表等设备提供更流畅的用户体验。随着Mate 40/P40等系列手机计划升级到HarmonyOS 2.0&#xff0c;用户可能需要将手机中的文件备…

芋道系统,springboot+vue3+mysql实现地址的存储与显示

1.效果图 2.前端实现&#xff1a; <el-form-item label"地址" prop"entrepriseAddress"><el-cascaderv-model"formData.entrepriseAddress"size"large":options"region"/></el-form-item> //导入组件 im…

Huawei 大型 WLAN 组网 AC 间漫游

AC1配置命令 <AC6005>display current-configuration # vlan batch 100 # interface Vlanif100description to_S3_CAPWAPip address 10.0.100.254 255.255.255.0 # interface GigabitEthernet0/0/1port link-type trunkport trunk allow-pass vlan 100# ip route-stati…

ETCD 备份与还原

安装etcdctl 准备看下etcd如何命令行操作&#xff0c;才发现&#xff0c;主机上&#xff0c;只用kubeadm拉起了etcd&#xff0c;但没有etcdctl命令。 # sudo docker ps -a | awk /etcd-master/{print $1} c4e3a57f05d7 26a11608b270 836dabc8e254 找到正在运行的etcd&#xf…

vscode中选择pytorch虚拟环境中库没有导入报错,但是本机命令行下载过了

这是下载成功的结果 这个时候你会发现matplotlib的库是下载过的&#xff0c;没法下载 这个的原因是你的matplotlib库是下载到本机的python上但是pytorch框架上的是没有这个库的&#xff0c;此时应该打开ananconda promopt 然后输入activate pytorch转换成pytorch环境 然后pip…

pip更新网络问题:Exception: Traceback (most recent call last): File

报错&#xff1a;rootdebian01:~# pip3.9 install --upgrade pip Collecting pip Downloading pip-24.0-py3-none-any.whl (2.1 MB) |██████████████████▉ | 1.2 MB 5.5 kB/s eta 0:02:39ERROR: Exception: Traceback (most recent call last): File “/usr…

MySQL的一些高频面试题汇总(持续补充)

1.事务4大特性 事务4大特性&#xff1a;原子性、一致性、隔离性、持久性 原⼦性&#xff1a;事务是最⼩的执⾏单位&#xff0c;不允许分割。事务的原⼦性确保动作要么全部完成&#xff0c;要么全不执行 一致性&#xff1a;执⾏事务前后&#xff0c;数据保持⼀致&#xff0c;多…