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.hqwc.cn/news/651333.html

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

相关文章

运行游戏提示dll文件丢失,分享多种有效的解决方法

在我们日常频繁地利用电脑进行娱乐活动&#xff0c;特别是畅玩各类精彩纷呈的电子游戏时&#xff0c;常常会遭遇一个令人困扰的问题。当我们满怀期待地双击图标启动心仪的游戏程序&#xff0c;准备全身心投入虚拟世界时&#xff0c;屏幕上却赫然弹出一条醒目的错误提示信息&…

【中级软件设计师】上午题12-软件工程(3):项目活动图、软件风险、软件评审、软件项目估算

【中级软件设计师】上午题12-软件工程&#xff08;3&#xff09; 1 软件项目估算1.1 COCOMO估算模型1.2 COCOMOⅡ模型 2 进度管理2.1 gantt甘特图2.2 pert图2.3 项目活动图2.3.1 画项目图 3 软件配置管理4 软件风险4.1 风险管理4.2 风险识别4.3 风险预测4.4 风险评估4.5 风险控…

Rest微服务案例

Rest 父工程构建microservicecloud-api公共子模块Modulemicroservicecloud-provider-dept-8001部门微服务提供者Modulemicroservicecloud-consumer-dept-80部门微服务消费者Module 以Dept部门模块做一个微服务通用案例 Consumer消费者&#xff08;Client&#xff09;通过REST调…

react项目发布后,浏览器源码泄露的解决方案

在使用create-react-app时&#xff0c;打包生产环境npm run build&#xff0c;浏览器打开后仍然是可以看到源码的。源码都没上传&#xff0c;为啥线上能看到源码 。 例&#xff1a;线上与服务器 线上与源码 react-scripts build和npm run build 有什么不同 react-scripts bui…

PID算法学习

PID算法介绍 在过程控制中&#xff0c;按偏差的比例&#xff08;P&#xff09;、积分&#xff08;I&#xff09;和微分&#xff08;D&#xff09;进行控制的PID控制器&#xff08;亦称PID调节器&#xff09;是应用最为广泛的一种自动控制器。它具有原理简单&#xff0c;易于实…

嵌入式Linux学习——Ubantu初体验

Ubuntu 和Windows 的最大差别 Windows中的每一个分区都对应着一个盘符&#xff0c;盘符下可以存放目录与文件&#xff0c;而在Ubantu中没有盘符的概念&#xff0c;只有目录结构。实际上不同的目录可能挂载在不同的分区之下&#xff0c;如果想要查看当前目录位于磁盘的哪个分区…

【java数据结构-优先级队列向下调整Topk问题,堆的常用的接口详解】

&#x1f308;个人主页&#xff1a;努力学编程’ ⛅个人推荐&#xff1a;基于java提供的ArrayList实现的扑克牌游戏 |C贪吃蛇详解 ⚡学好数据结构&#xff0c;刷题刻不容缓&#xff1a;点击一起刷题 &#x1f319;心灵鸡汤&#xff1a;总有人要赢&#xff0c;为什么不能是我呢 …

递归的层序遍历

最近遇到一个业务需求&#xff1a;一颗依赖树&#xff0c;其实就是一颗递归树&#xff0c;如何一层一层的数据放在一起&#xff0c;可以近似理解为二叉树的层序遍历。 业务理解为递归树的层序遍历 代码示例&#xff1a; public class RecursionErgodic {public static void…

使用Kimi的一些体会

1、https://kimi.cn 这个回答问题还比较专业&#xff0c;感觉比以前chatgpt要好一些 2、Moonshot AI - 开放平台 可以通过注册账号&#xff0c;或微信扫描就可以登录进去 通过postman可以体会一下功能 2.1 POST https://api.moonshot.cn/v1/chat/completions 2.2 授权选择下…

达梦(DM) SQL日期操作及分析函数

达梦DM SQL日期操作及分析函数 日期操作SYSDATEEXTRACT判断一年是否为闰年周的计算确定某月内第一个和最后一个周末某天的日期确定指定年份季度的开始日期和结束日期补充范围内丢失的值按照给定的时间单位查找使用日期的特殊部分比较记录 范围处理分析函数定位连续值的范围查找…

docker部署通义千问-7B-Chat的openai-api环境

服务器环境&#xff1a; 显卡驱动&#xff1a;Driver Version: 530.30.02 CUDA版本&#xff1a;CUDA Version: 12.1 显卡&#xff1a;NVIDIA GeForce RTX 3090共4张 注意&#xff1a;最好把显卡驱动升级到530&#xff0c;CUDA版本之前使用11.7有问题。 一、下载模型文件 …

环境配置——Windows平台配置VScode运行环境为远程服务器或虚拟机

1. 远程机需要先安装SSH服务&#xff0c;命令如下 sudo apt install openssh-server 2. 安装好后需要开启SSH服务&#xff1a; sudo service sshd start 3. 查看SSH服务是否有被开启&#xff1a; sudo systemctl status sshd.service 4. 本地Windows需要生成密钥将公钥放…