嵌入式Linux-线程创建与终止

news2024/9/26 5:13:15

1. 线程的创建

1.1 创建线程

启动程序时,创建的进程只是一个单线程的进程,称之为初始线程或主线程,本小节我们讨论如何创建一个新的线程。

创建线程与创建进程的方法是一样的,让我们来看一下创建线程的函数:

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

我们来解读一下参数的含义:

参数含义
threadpthread_t类型指针,当pthread_create()成功返回时,新创建的线程的线程ID会保存在参数thread
attrpthread_attr_t类型指针,指向pthread_attr_t类型的缓冲区,pthread_attr_t数据类型定义了线程的各种属性,如果将参数attr设置为NULL,那么表示将线程的所有属性设置为默认值,以此创建新线程。
start_routine参数start_routine是一个函数指针,指向一个函数,新创建的线程从start_routine()函数开始运行,该函数返回值类型为void*,并且该函数的参数只有一个void*,其实这个参数就是pthread_create()函数的第四个参数arg
arg传递给start_routine()函数的参数。一般情况下,需要将arg指向一个全局或堆变量,意思就是说在线程的生命周期中,该arg指向的对象必须存在,否则如果线程中访问了该对象将会出现错误。当然也可将参数arg设置为NULL,表示不需要传入参数给start_routine()函数。
返回值成功返回0;失败时将返回一个错误号,并且参数thread指向的内容是不确定的。

注意pthread_create()在调用失败时通常会返回错误码,它并不像其它库函数或系统调用一样设置errno,每个线程都提供了全局变量errno的副本,这只是为了与使用errno到的函数进行兼容,在线程中,从函数中返回错误码更为清晰整洁,不需要依赖那些随着函数执行不断变化的全局变量,这样可以把错误的范围限制在引起出错的函数中。

线程创建成功,新线程就会加入到系统调度队列中,获取到CPU之后就会立马从start_routine()函数开始运行该线程的任务;调用pthread_create()函数后,通常我们无法确定系统接着会调度哪一个线程来使用CPU资源,先调度主线程还是新创建的线程呢(而在多核CPU或多CPU系统中,多核线程可能会在不同的核心上同时执行)? 如果程序对执行顺序有强制要求,那么就必须采用一些同步技术来实现。这与前面学习父、子进程时也出现了这个问题,无法确定父进程、子进程谁先被系统调度。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
static void *new_thread_start(void *arg)
{
	 printf("新线程: 进程 ID<%d> 线程 ID<%lu>\n", getpid(), pthread_self());
	 return (void *)0;
}

int main(void)
{
	 pthread_t tid;
	 int ret;
	 ret = pthread_create(&tid, NULL, new_thread_start, NULL);
	 if (ret) 
	 {
		 fprintf(stderr, "Error: %s\n", strerror(ret));
		 exit(-1);
	 }
	 printf("主线程: 进程 ID<%d> 线程 ID<%lu>\n", getpid(), pthread_self());
	 sleep(1);
	 exit(0);
}

应该将pthread_t作为一种不透明的数据类型加以对待,但是在示例代码中需要打印线程ID,所以要明确其数据类型,示例代码中使用了printf()函数打印线程ID时,将其作为unsignedlongint数据类型,在Linux 系统下,确实是使用unsignedlongint来表示pthread_t,所以这样做没有问题!
主线程休眠了1秒钟,原因在于,如果主线程不进行休眠,它就可能会立马退出,这样可能会导致新创建的线程还没有机会运行,整个进程就结束了。
在主线程和新线程中,分别通过getpid()和pthread_self()来获取进程ID和线程ID,将结果打印出来,运行结果如下所示:
在这里插入图片描述
编译时出现了错误,提示“对‘pthread_create’未定义的引用”,示例代码确实已经包含了<pthread.h>头文件,但为什么会出现这样的报错,仔细看,这个报错是出现在程序代码链接时、而并非是编译过程,所以可知这是链接库的文件,如何解决呢?
在这里插入图片描述
从打印信息可知,正如前面所介绍那样,两个线程的进程ID相同,说明新创建的线程与主线程本来就属于同一个进程,但是它们的线程ID不同。从打印结果可知,Linux系统下线程ID数值非常大,看起来像是一个指针。

2. 线程ID

2.1 不可或缺的线程ID

就像每个进程都有一个进程ID一样,每个线程也有其对应的标识,称为线程ID。进程ID在整个系统中是唯一的,但线程ID不同,线程ID只有在它所属的进程上下文中才有意义。

进程ID使用pid_t数据类型来表示,它是一个非负整数。而线程ID使用pthread_t数据类型来表示,一个线程可通过库函数pthread_self()来获取自己的线程ID,其函数原型如下所示:

#include <pthread.h>

pthread_t pthread_self(void);

3. 终止线程

3.1 如何终止线程

在新线程的启动函数(线程 start 函数)new_thread_start()通过 return 返回之后,意味着该线程已经终止了,除了在线程 start 函数中执行 return 语句终止线程外,终止线程的方式还有多种,可以通过如下方式终止线程的运行:

  1. 线程的 start 函数执行 return 语句并返回指定值,返回值就是线程的退出码;
  2. 线程调用 pthread_exit()函数;
  3. 调用 pthread_cancel()取消线程

tips:这里万分注意的是,慎在线程中使用exit()、_exit()或者_Exit(),否则,你使用的整个程序都会退出,后果看个人的使用场景吧!

我们来看一下pthread_exit()函数将终止调用它的线程,其函数原型如下所示:

#include <pthread.h>

void pthread_exit(void *retval);

参数 retval 的数据类型为 void *,指定了线程的返回值、也就是线程的退出码,该返回值可由另一个线程通过调用 pthread_join()来获取;同理,如果线程是在 start 函数中执行 return 语句终止,那么 return 的返回值也是可以通过 pthread_join()来获取的。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>

static void *new_thread_start(void *arg)
{
	 printf("新线程 start\n");
	 sleep(1);
	 printf("新线程 end\n");
	 pthread_exit(NULL);
}

int main(void)
{
	 pthread_t tid;
	 int ret;
	 ret = pthread_create(&tid, NULL, new_thread_start, NULL);
	 if (ret) 
	 {
		 fprintf(stderr, "Error: %s\n", strerror(ret));
		 exit(-1);
	 }
	 printf("主线程 end\n");
	 pthread_exit(NULL);
	 exit(0);
}

在这里插入图片描述
正如上面介绍到,主线程调用 pthread_exit()终止之后,整个进程并没有结束,而新线程还在继续运行。所以进程里的那几个退出函数,大家一定要注意使用呀,可能有时候程序莫名其妙退出,但是检查半天,又能编译成功,但是就是运行不完整程序,会花费大量的时间!

2023年了,祝大家新年快乐!
本文参考正点原子的嵌入式LinuxC应用编程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/175604.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux基本功系列之chown命令实战

文章目录一. 前言&#x1f680;&#x1f680;&#x1f680;二. chown命令介绍三. 语法格式及常用选项四. 参考案例3.1 改变指定文件的属组和属主3.2 改变指定文件的所属主与所属组&#xff0c;并显示过程3.3 改变指定目录及其内所有子文件的所属主与所属组3.4 只修改文件所属组…

P问题、NP问题、NP-Complete问题、NP-Hard问题分别代表什么含义?

绪论 在了解P、NP、NP-Complete、NP-Hard问题之前,先感性地感受一下这几个问题之间的区别和联系👇: 上图分为左右两个版本,推荐记住左边的比较通用。这是因为NP=P这个数学问题曾经被列为7大数学难题之一,而且是之首,甚至美国还悬赏100W美金,但是比较认可的结果是暂时…

Python数据可视化之条形图和热力图

Python数据可视化之条形图和热力图 提示&#xff1a;介绍 简单介绍Pthon可视化的图表使用 提示&#xff1a;热力图和条形图 文章目录Python数据可视化之条形图和热力图前言一、导入数据包二、选择数据集2.加载数据2.读入数据总结前言 提示&#xff1a;这里可以添加本文要记录的…

Acwing 1010. 拦截导弹

Acwing 1010. 拦截导弹一、问题描述二、算法分析三、代码实现一、问题描述 二、算法分析 这道题共分为两问&#xff0c;我们先看第一问。 该问的背后是一个很经典的最长单调子序列模型。 在这个模型中&#xff0c;我们的状态f[i]f[i]f[i]的定义是&#xff0c;以第iii个元素为结…

Cert Manager 申请SSL证书流程及相关概念-三

中英文对照表 英文英文 - K8S CRD中文备注certificatesCertificate证书certificates.cert-manager.io/v1certificate issuersIssuer证书颁发者issuers.cert-manager.ioClusterIssuer集群证书颁发者clusterissuers.cert-manager.iocertificate requestCertificateRequest证书申…

50个你离不开的 CLI 工具

作为开发人员&#xff0c;我们在终端上花费了大量时间。有很多有用的 CLI 工具&#xff0c;它们可以让您在命令行中的生活更轻松、更快速&#xff0c;而且通常更有趣。这篇文章概述了我最依赖的 50 个必备 CLI 工具。如果我遗漏了什么 - 请在评论中告诉我 :)在本文的结尾&#…

二叉树专题汇总

二叉树的前中后序遍历day11|144.二叉树的前序遍历、145.二叉树的后序遍历、94.二叉树的中序遍历_奈川直子的博客-CSDN博客二叉树的层序遍历、翻转二叉树、对称二叉树day12|层序遍历合集、226.翻转二叉树、101.对称二叉树_奈川直子的博客-CSDN博客N叉树最大深度、完全二叉树节点…

LCHub:2023年无代码、低代码8大技术趋势

在数字化转型、新冠疫情、全球经济环境动荡等多因素驱动下,使得组织改变了对低代码、无代码的看法,市场规模和场景化落地实现快速增长。根据Gartner预测,到2025年,70%的应用程序将由低代码和无代码开发平台构建,其中 80%的应用程序由非IT工作者开发而成。 没有编程背景的…

青山不语,碧海无痕,数字山河间的中国脊梁

刚刚过去的2022&#xff0c;对于新农人来说&#xff0c;是不寻常的一年。福建宁德的渔民祖祖辈辈都以外出打鱼为生&#xff0c;渔排生活条件艰苦&#xff0c;娱乐匮乏&#xff0c;销路也不稳定&#xff0c;这种苦闷正随着AI、直播等数字新技术的到来而被消解。青海列卜加村的孙…

Arduino的nodemcu 8266开发板使用MicroPython开发的整体流程

程序安装准备 安装开发板驱动&#xff0c;官网&#xff1a;&#xff08;https://cn.silabs.com/developers/usb-to-uart-bridge-vcp-drivers?tabdownloads&#xff09;这里不是CH340驱动&#xff0c;而是CP210x USB to USART 驱动&#xff0c;最终也是在“设备管理器查看COM口…

【LeetCode:33. 搜索旋转排序数组~~~二分】

题目描述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…

操作系统真相还原_第5章第1节:利用BIOS中断0x15 获取内存信息

文章目录功能号返回信息功能号详解子功能号&#xff1a;0xE820子功能号&#xff1a;0xE801子功能号&#xff1a;0x88内存检测示例当前内存分布说明程序boot.incmbr.sloader.s编译并写入硬盘启动bochs运行功能号 EAX0xE820&#xff1a;遍历主机上全部内存 AX0xE801&#xff1a;…

分享121个ASP源码,总有一款适合您

ASP源码 分享121个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 121个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1wwsIoZidHihm5WHPKjlvMg?pwdew3b 提取码&#x…

如何在linux服务器部署sonar服务?

文章目录前言一、上传sonar插件二、设置sonar插件的权限三、启动插件四、配置环境变量前言 sonar服务是代码质量扫描的工具,我们如何利用sonar进行代码质量扫描还有在linux服务器上部署呢?下面这篇文章详细的记录了我在sonar的使用过程中所遇到的各种坑。 一、上传sonar插件…

十大经典排序算法【算法思想+图解+代码】【数据结构与算法笔记】

前言&#xff1a;文中大部分为本人收集整理&#xff0c;综合学习资料&#xff0c;个人理解……。希望能帮助你少掉写头发&#xff0c;早日走出理解的深渊。因为写作较为仓促文中内容难免会有纰漏&#xff0c;发现可评论区回复&#xff08;无奖&#xff09;。排序(Sort)分析排序…

StudioOne6旗舰版DAW2023跨版本升级新功能介绍

PreSonus2023发布了Studio One 6旗舰级DAW的跨版本升级。他们获奖的DAW软件的这一最新版本引入了大量的新功能和增强功能&#xff0c;包括用户界面的定制选项&#xff0c;PreSonus说这使第6版成为他们迄今为止最个性化的升级。PreSonus发布了旗舰级DAW的跨版本升级&#xff0c;…

Leetcode:131. 分割回文串(C++)

目录 问题描述&#xff1a; 实现代码与解析&#xff1a; 回溯&#xff1a; 原理思路&#xff1a; 问题描述&#xff1a; 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。 回文串 是正着读和反着读都…

客快物流大数据项目(一百零六):实时ETL处理

文章目录 实时ETL处理 一、业务流程 二、​​

Kettle简介

一、Kettle基本介绍 Kettle(现更名为Pentaho Data Integration-Pentaho)是一款国外开源的ETL工具&#xff0c;纯java编写&#xff0c;可以在Window、Linux、Unix上运行&#xff0c;绿色无需安装。它允许你管理来自不同数据库的数据&#xff0c;通过提供一个图形化的用户环境来…

盒模型应用 改变宽高范围 改变背景覆盖范围 溢出处理 断词规则 空白处理

目录盒模型应用改变宽高范围改变背景覆盖范围溢出处理断词规则 word-break空白处理 溢出的空白处理盒模型应用 改变宽高范围 默认情况下&#xff0c;width 和 height 设置的是内容盒宽高。 页面重构师&#xff1a;将psd文件&#xff08;设计稿&#xff09;制作为静态页面 衡量…