Linux之线程管理

目录

第1关:创建线程

任务描述

相关知识

使用pthread_create函数创建线程

编程要求

答案:

第2关:线程挂起

任务描述

相关知识

使用pthread_join挂起线程

编程要求

答案:

第3关:线程终止

任务描述

相关知识

使用pthread_exit退出线程

使用pthread_cancel退出线程

编程要求

答案: 


第1关:创建线程

任务描述

通常我们编写的程序都是单进程,如果在一个进程中没有创建新的线程,则这个单进程程序也就是单线程程序。本关我们将介绍如何在一个进程中创建多个线程。

本关任务:学会使用C语言在Linux系统中使用pthread_create库函数创建一个新的线程。

相关知识

通常我们编写的程序都是单线程程序,单线程的程序都是按照一定的顺序按序的执行。有些情况下,我们需要在一个进程中同时执行多个控制流程,这时候线程就派上了用场。例如,我们需要实现一个在线音乐播放器,一方面我们在线播放用户选中的音乐,另一方面又需要同时下载曲子,这些任务需要同时被执行,而不是按序一个一个的执行,这样才会使得用户一边播放音乐,一边下载自己喜欢的曲子。针对以上需求,我们可以用多线程实现,一个线程专门在线播放用户选中的音乐,另外一个线程专门用户下载曲子。

通常,一个进程只包含一个线程,我们把这个线程叫做主线程,例如main函数就是一个主线程。如果在主线程里创建多个线程,那么程序就会在创建线程的地方产生分支,变成了多个程序来同时运行。这似乎和我们以前学习的多进程一样,其实背后的原理还是有所区别。

在多进程中,子进程是通过拷贝父进程的地址空间来实现,而在多线程中,同一进程中的所有线程都是共享程序代码,一段代码可以被多个线程来执行。

Linux系统中,我们可以通过pthread_create函数来创建线程。我们可以使用man命令来查询该函数的使用方法。具体的查询命令为:man 3 pthread_create

使用pthread_create函数创建线程

pthread_create函数的具体的说明如下:

需要的头文件如下:

#include <pthread.h>

函数格式如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
thread:该参数是一个指针,当线程创建成功后,用来返回创建的线程ID;
attr:该参数用于指定线程的属性,NULL表示使用默认属性,通常我们使用默认属性;
start_routine:该参数为一个函数指针,指向线程创建后要调用的函数,也被称为线程函数;
arg:该参数指向传递给线程函数的参数;

函数返回值说明: 调用成功,pthread_create返回值为0;调用失败返回一个非零的值。

注意:

pthread_create一旦调用成功,新创建的线程将开始运行第3个参数所指向的函数,原来的线程继续往下运行。由于线程是第三库所提供的,因此在编译包含线程的程序时,我们需要手动链接线程库,只需在编译命令加上-lpthread参数即可。

案例演示1:

编写一个程序,使用pthread_create函数创建一个线程,在新创建的线程中打印一个字符串,在主线程中也打印一个字符串。详细代码如下所示:
 

#include <stdio.h>
#include <pthread.h>
void *printString(void *arg)
{printf("This is My first thread\n");return NULL;
}
int main()
{pthread_t thread;int ret = pthread_create(&thread, NULL, printString, NULL);if(ret != 0){printf("创建线程失败\n");return -1;}sleep(1);printf("This is main thread\n");return 0;
}

将以上代码保存为createThread.c文件,编译执行。可以看到新创建的线程被调用成功。

案例演示2:

编写一个程序,使用pthread_create函数创建两个线程,并在每个线程中接受主线程传来的字符串,并将其打印出来。详细代码如下所示:
 

#include <stdio.h>
#include <pthread.h>
void *printString(void *arg)
{printf("print String: %s\n", (char *)arg);return NULL;
}
int main()
{pthread_t thread1, thread2;int ret = pthread_create(&thread1, NULL, printString, "This is first thread");if(ret != 0){printf("创建线程失败\n");return -1;}ret = pthread_create(&thread2, NULL, printString, "This is second thread");if(ret != 0){printf("创建线程失败\n");return -1;}sleep(1);printf("This is main thread\n");return 0;
}

将以上代码保存为printThread.c文件,编译执行。可以看到新创建的按照主线程传递的参数,将指定的字符串打印出来。

注意:编译程序的时候需要手动加上线程库-lpthread。否则,编译时会出错。

编程要求

本关的编程任务是补全右侧代码片段中BeginEnd中间的代码,具体要求如下:

补全createThread函数,使用pthread_create函数创建线程,并将start_routine作为线程处理函数,arg作为线程处理函数的参数,同时将创建成功的线程ID作为createThread函数的返回值。

答案:
#include <stdio.h>
#include <pthread.h>/************************* 参数start_routine: 函数指针,用于指向线程函数* 参数arg: 是线程函数的参数* 返回值: 返回线程ID
*************************/
pthread_t createThread(void *(*start_routine) (void *), void *arg)
{pthread_t thread;/********** BEGIN **********/int res = pthread_create(&thread, NULL, start_routine, arg);if (res != 0){return 0;}/********** END **********/return thread;
}

第2关:线程挂起

任务描述

在学习多进程编程的时候,我们学习了如何等待一个进程结束,那么在多线程中也存在同样的操作,如何使得一个线程挂起等待其他的线程先执行。本关我们将介绍如何挂起一个线程,并等待指定线程。

本关任务:学会使用C语言在Linux系统中使用pthread_join库函数挂起当前线程,并等待指定的线程。

相关知识

通过上一管卡的学习,我们学会了如何创建一个线程。在上一关中我们遗留了一个未解决的问题,不知道细心的你发现没,那就是我们在案例演示中使用了sleep函数给主线程睡眠了1秒,如果主线程先退出,那么新创建的线程会发生什么?正确答案是,如果主线程先退出,那么还未执行完成的其他所有线程将被终止。因此,保证主线程最后一个退出是非常重要的。

接下来,我们用实例验证如果主线程先退出,那么其他的线程会不会受到影响,将上一关中的案例一修改成如下:

#include <stdio.h>
#include <pthread.h>
void *printString(void *arg)
{sleep(1);printf("This is My first thread\n");return NULL;
}
int main()
{pthread_t thread;int ret = pthread_create(&thread, NULL, printString, NULL);if(ret != 0){printf("创建线程失败\n");return -1;}printf("This is main thread\n");return 0;
}

编译执行。可以看到当主线程退出后,新创建的线程是不会继续执行的。

Linux系统中提供了挂起当前线程,用来等待一个指定线程结束的库函数pthread_join。这个函数相当于进程等待函数waitpid,他可以挂起当前线程,并一直等待指定线程,直到指定线程退出后,该函数才会返回继续执行。

Linux系统中,我们可以通过pthread_join函数来挂起线程。我们可以使用man命令来查询该函数的使用方法。具体的查询命令为:man 3 pthread_join

使用pthread_join挂起线程

pthread_join函数的具体的说明如下:

需要的头文件如下:

#include <pthread.h>

函数格式如下:

int pthread_join(pthread_t thread, void **retval);

参数说明:

thread:该参数是一个线程ID,用于指定要等待其终止的线程;
retval:该参数用于存放等待线程的返回值,如果不关注线程的退出值,则可以设置为NULL;

函数返回值说明: 调用成功,pthread_join返回值为0;调用失败返回一个非零的值。

注意:

由于线程是第三库所提供的,因此在编译包含线程操作的程序时,我们需要手动链接线程库,只需在编译命令加上-lpthread参数即可。

案例演示1:

编写一个程序,使用pthread_join函数挂起当前线程,等待新创建的线程先执行。详细代码如下所示:

#include <stdio.h>
#include <pthread.h>
void *printString(void *arg)
{sleep(1);printf("This is My first thread\n");return NULL;
}
int main()
{pthread_t thread;int ret = pthread_create(&thread, NULL, printString, NULL);if(ret != 0){printf("创建线程失败\n");return -1;}if(pthread_join(thread, NULL) != 0){printf("等待线程失败\n");return -1;}printf("This is main thread\n");return 0;
}

将以上代码保存为joinThread.c文件,编译执行。可以看到尽管新创建的线程睡眠了1秒,然后还是被正常的运行完。 注意:编译程序的时候需要手动加上线程库-lpthread。否则,编译时会出错。

编程要求

本关的编程任务是补全右侧代码片段中BeginEnd中间的代码,具体要求如下:

补全waitThread函数,使用pthread_join函数挂起当前线程,等待指定线程结束,thread为要等待的线程ID号,waitThread函数等待线程成功返回0,失败返回-1

答案:
#include <stdio.h>
#include <pthread.h>/************************* 参数thread: 需要等待结束的线程ID号* 返回值: 等待成功返回0,失败返回-1* 提示: 忽略线程返回值
*************************/
int waitThread(pthread_t thread)
{int ret = -1;/********** BEGIN **********/ret = pthread_join(thread, NULL);/********** END **********/return ret;
}

第3关:线程终止

 

任务描述

在学习多进程编程的时候,我们知道进程的退出有很多中方式,常见的有exit函数,而线程的退出也有多种方法。本关我们将介绍如何终止一个线程的执行。

本关任务:学会使用C语言在Linux系统中终止一个线程。

相关知识

Linux下有三种方式可以是一个线程终止,分别是:(1)通过return从线程函数返回;(2)通过调用pthread_exit使得一个线程退出;(3)通过调用pthread_cancel终止一个线程;

有两种特殊情况要注意:第一种情况就是,在主线程中,如果从main函数返回或者是调用exit函数来终止主线程的执行,则整个进程将终止执行,此时进程中的所有线程也将被终止执行,因此,在主线程中不能过早的退出,这就是我们上一关中所介绍的为什么要使用pthread_join函数来挂起主线程的原因。另一种情况就是,如果在主线程中调用pthread_exit函数终止主线程的执行,则仅仅是主线程消亡,进程是不会被终止的,因此进程内的其他线程也不会被终止,直到所有线程执行结束,进程才会被终止。

在上一关中,我们学习了pthread_join函数等待一个线程的结束,并获取线程退出值,那么线程以不同的方法终止,通过pthread_join得到的退出值也是不同的,总结如下:
 

如果线程通过调用return返回,则pthread_join所得到的退出值就是线程函数的return的值;
如果线程是通过调用pthread_cancel异常终止,则pthread_join所得到的退出值是PTHREAD_CANCELED;
如果线程是通过调用pthread_exit异常终止,则pthread_join所得到的退出值就是pthread_exit函数的参数值;

我们可以使用man命令来查询这些函数的使用方法。具体的查询命令为:man 3 函数名

使用pthread_exit退出线程

pthread_exit函数的具体的说明如下:

需要的头文件如下:

#include <pthread.h>

函数格式如下:

void pthread_exit(void *retval);

参数说明:

retval:线程的返回值;

函数返回值说明: 无返回值。

注意:

由于线程是第三库所提供的,因此在编译包含线程操作的程序时,我们需要手动链接线程库,只需在编译命令加上-lpthread参数即可。

案例演示1:

编写一个程序,使用pthread_exit函数退出线程,并使用pthread_join函数获取线程的退出值。详细代码如下所示:

#include <stdio.h>
#include <pthread.h>
void *printString(void *arg)
{sleep(1);printf("This is My first thread\n");pthread_exit("thread finished");
}
int main()
{pthread_t thread;int ret = pthread_create(&thread, NULL, printString, NULL);if(ret != 0){printf("创建线程失败\n");return -1;}void *status = NULL;if(pthread_join(thread, &status) != 0){printf("等待线程失败\n");return -1;}printf("first thread exit(%s)\n", (char *)status);printf("This is main thread\n");return 0;
}

将以上代码保存为exitThread.c文件,编译执行。可以看到新创建的线程退出的代码为:"thread finished",并且在主线程中使用pthread_join函数成功的获取到退出代码。 注意:编译程序的时候需要手动加上线程库-lpthread。否则,编译时会出错。

使用pthread_cancel退出线程

pthread_cancel函数的具体的说明如下:

需要的头文件如下:

#include <pthread.h>

函数格式如下:

int pthread_cancel(pthread_t thread);

参数说明:

thread:需要被取消运行的线程ID;

函数返回值说明: 调用成功,返回0,调用失败,返回一个非零值。

注意:

由于线程是第三库所提供的,因此在编译包含线程操作的程序时,我们需要手动链接线程库,只需在编译命令加上-lpthread参数即可。

案例演示1:

编写一个程序,使用pthread_cancel函数退出线程,并使用pthread_join函数获取线程的退出值。详细代码如下所示:

#include <stdio.h>
#include <pthread.h>
void *printString(void *arg)
{while(1){printf("This is My first thread\n");sleep(1);}
}
int main()
{pthread_t thread;int ret = pthread_create(&thread, NULL, printString, NULL);if(ret != 0){printf("创建线程失败\n");return -1;}sleep(2);ret = pthread_cancel(thread);if(ret != 0){printf("cancel thread(%lu) failure\n", thread);return -1;}void *status = NULL;if(pthread_join(thread, &status) != 0){printf("等待线程失败\n");return -1;}printf("first thread exit(%d)\n", (int)status);printf("This is main thread\n");return 0;
}

将以上代码保存为cancelThread.c文件,编译执行。可以看到新创建的线程被主线程使用pthread_cancel函数强制取消执行,并且退出的代码为:-1,也就是PTHREAD_CANCELED在Linux系统中的定义为-1

注意:

编译程序的时候需要手动加上线程库-lpthread。否则,编译时会出错。

编程要求

本关的编程任务是补全右侧代码片段中BeginEnd中间的代码,具体要求如下:

补全cancelThread函数,使用pthread_cancel函数终止指定的线程,thread为线程要被取消的线程ID号,调用成功返回0,否则返回-1

答案: 
#include <stdio.h>
#include <pthread.h>/************************* 参数thread: 需要等待结束的线程ID号* 返回值: 等待成功返回0,失败返回-1* 提示: 忽略线程返回值
*************************/
int cancelThread(pthread_t thread)
{int ret = -1;/********** BEGIN **********/ret = pthread_cancel(thread);/********** END **********/return ret;
}

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

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

相关文章

18种WEB常见漏洞:揭秘网络安全的薄弱点

输入验证漏洞: 认证和会话管理漏洞: 安全配置错误: 其他漏洞: 防范措施: Web 应用程序是现代互联网的核心&#xff0c;但它们也容易受到各种安全漏洞的影响。了解常见的 Web 漏洞类型&#xff0c;对于开发人员、安全测试人员和普通用户都至关重要。以下将介绍 18 种常见的 …

MySQL—MySQL的存储引擎之InnoDB

MySQL—MySQL的存储引擎之InnoDB 存储引擎及种类 存储引擎说明MyISAM高速引擎&#xff0c;拥有较高的插入&#xff0c;查询速度&#xff0c;但不支持事务InnoDB5.5版本后MySQL的默认数据库存储引擎&#xff0c;支持事务和行级锁&#xff0c;比MyISAM处理速度稍慢ISAMMyISAM的…

Android Studio查看viewtree

前言&#xff1a;之前开发过程一直看的是手机上开发者选项中的显示布局边界&#xff0c;开关状态需要手动来回切换&#xff0c;今天偶然在Android Studio中弄出了布局树觉得挺方便的。

JPEG图像常用加密算法简介

JPEG图像加密算法 目前&#xff0c;JPEG图像加密算法可以分成异或加密、置乱加密和置乱与异或组合加密。下面对这三种加密方式进行阐述。 (1) 异或加密 文献[1]提出了一种基于异或加密的JPEG图像的RDH-EI方案。该算法通过对AC系数的ACA和图像的量化表进行流密码异或&#xf…

代码随想录训练营Day 33|Python|Leetcode|● 理论基础 ● 509. 斐波那契数 ● 70. 爬楼梯 ● 746. 使用最小花费爬楼梯

理论基础 动态规划五步曲 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 509. 斐波那契数 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始…

vue3——笔记2(计算属性,类与样式绑定)

计算属性 在 Vue3 中&#xff0c;计算属性的用法和 Vue2 基本上是一样的&#xff0c;但是在性能上有了一些改进。Vue3 中计算属性是通过computed函数来创建的&#xff0c;计算属性的值会在相关依赖发生改变时自动更新。与 Vue2 相比&#xff0c;Vue3 的计算属性在一些场景下会…

某翻译平台翻译接口逆向之webpack学习

逆向网址 aHR0cHM6Ly9mYW55aS55b3VkYW8uY29tLw 逆向链接 aHR0cHM6Ly9mYW55aS55b3VkYW8uY29tLyMv 逆向接口 aHR0cHM6Ly9kaWN0LnlvdWRhby5jb20vd2VidHJhbnNsYXRl 逆向过程 请求方式 POST 逆向参数 sign c168e4cb76169e90f82d28118dbd24d2 接口请求结果解密 过程分析 根据XHR…

大数据第七天

文章目录 吐槽一下这个是怎么需要真的这么大吗? 内核错误内核软死锁&#xff08;soft lockup&#xff09;我这个cpu很高吗?大模型都说了不超过80就行了 FinBi安装FinBI下载链接安装时间比较长 吐槽一下 dbeaver 查询hive 数据信息是真的慢&#xff0c;没有一点快的方式&…

【优秀AI项目】每日跟踪 OpenVoice ,AI快站,OpenVoice

持续更新好玩的开源AI项目或AI商业应用体验 一起来玩转AI&#xff01;&#xff01; 1 huggingface 国内镜像站&#xff1a;AI 快站 HUggingface被墙了&#xff0c;emmmmm 所以我之前玩模型的一大感觉就是 下载什么模型之类的太难受了&#xff01;服了 看到一个镜像站——…

在Visio中插入半圆状箭头

在 Microsoft Visio 中&#xff0c;你可以通过以下步骤来绘制一个带箭头的半圆&#xff1a; 1.打开 Visio&#xff1a;打开 Microsoft Visio 软件。 2.选择绘图类型&#xff1a;在 Visio 中&#xff0c;你可以选择使用“基本形状”或“箭头线”工具来绘制带箭头的半圆。 使用…

文件权限管理

文件权限管理 1. 权限对象 权限对象含义u属主&#xff0c;所有者g属组o其他人 2. 权限类型 权限类型含义值r读权限4w写权限2x执行权限1 3. 修改文件属主及属组 命令:chown(change own)更改文件或目录属主与属组名 3.1 修改文件属主与属组 只修改属主&#xff1a;chown $…

Open CASCADE学习|一个点的坐标变换

gp_Trsf 类是 Open CASCADE Technology (OCCT) 软件库中的一个核心类&#xff0c;用于表示和操作三维空间中的变换。以下是该类的一些关键成员和方法的介绍&#xff1a; 成员变量&#xff1a; scale: Standard_Real 类型&#xff0c;表示变换的缩放因子。 shape: gp_TrsfFor…

Android11 SystemUI clock plugin 插件入门

插件的编写 参照ExamplePlugin&#xff0c;需要系统签名。 需要先编译以下模块得到jar&#xff0c;引用在项目中。 m SystemUIPluginLibcom.android.systemui.permission.PLUGIN PluginManager.addPluginListener SystemUI 是如何发现 clock plugin 的&#xff1f; Syste…

FDY10蓄电池容量检测仪

FDY10-H说明书2013 08.pdf (book118.com)https://max.book118.com/html/2017/0510/105769526.shtm FDY10用户手册 - 百度文库 (baidu.com)https://wenku.baidu.com/view/22e7fe672d3f5727a5e9856a561252d380eb20ac?aggId28d62908f12d2af90242e62a&frcatalogMain_graph_v10…

ThingsBoard处理设备上报的属性并转换为可读属性

一、前言 二、案例 1、AI生成JSON数据体 2、将json数据体直接通过遥测topic发送查看效果 3、可查看目前整个数据都在一起 ​编辑 4、配置附规则链路 5、对msg的消息值&#xff0c;进行数据的转换&#xff0c;并从新进行赋值。 6、规则链路关联关系 7、再次通过MQTT发送遥…

WebGIS

文章目录 GIS的全名是Geographic Information System&#xff0c;中文全名是地理信息系统。 它是在计算机硬、软件系统支持下&#xff0c;对整个或部分地球表层&#xff08;包括大气层&#xff09;空间中的有关地理分布数据进行采集、储存、管理、运算、分析、显示和描述的技术…

详解23种设计模式——单例模式

单例模式 | CoderMast编程桅杆单例模式 单例模式是最常用的设计模式之一&#xff0c;他可以保证在整个应用中&#xff0c;某个类只存在一个实例化对象&#xff0c;即全局使用到该类的只有一个对象&#xff0c;这种模式在需要限制某些类的实例数量时非常有用&#xff0c;通常全局…

【GitHub】如何在github上提交PR(Pull Request) + 多个pr同时提交、互不干扰

【GitHub】如何在github上提交PR(Pull Request 写在最前面1. 准备工作1.1 注册 GitHub 账号1.2 了解 Git 基础1.3 找到一个项目 2. 创建你的 PR2.1 Fork 和克隆仓库2.2 创建一个新的分支2.3 进行更改2.4 推送更改到 GitHub2.5 创建 Pull Request 3. 优化你的 PR3.1 保持提交清晰…

JetBot手势识别实验

实验简介 本实验目的在JetBot智能小车实现手势识别功能&#xff0c;使用板卡为Jetson Nano。通过小车摄像头&#xff0c;识别五个不同的手势&#xff0c;实现小车的运动及灯光控制。 1.数据采集 连接小车板卡的Jupyterlab环境&#xff0c;运行以下代码块&#xff0c;配置数据…

Vitis HLS 学习笔记--C/C++ static 关键字的作用

目录 1. 简介 2. c/c共有性质 3. c独有性质 4. 示例说明 5. static 对于 HLS 工具的影响 6. 总结 1. 简介 在Vitis HLS中&#xff0c;偶尔会用到 static 关键字。考虑到Vitis HLS同时兼容C和C语言&#xff0c;有必要理解这两种语言中static关键字细微差异。本文旨在梳理…