Linux系统编程(五)多线程创建与退出

目录

  • 一、基本知识点
  • 二、线程的编译
  • 三、 线程相关函数
    • 1. 线程的创建
      • (1)整型的传入与接收
      • (2)浮点数的传入与接收
      • (3)字符串的传入与接收
      • (4)结构体的传入与接收
    • 总结
    • 2. 线程的退出
    • 3. 线程的等待
    • 补充
      • (1)返回整型
      • (2)返回浮点数
      • (3)返回字符串
      • (4)返回结构体
  • 四、综合举例

  

一、基本知识点

  1. 定义
       线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。每一个进程都至少包含一个main线程。
       相比于进程而言,线程更加轻量级,因为它们共享了进程的地址空间以及其他资源,所以线程之间的切换和通信会更加高效。一个进程可以包含多个线程,这些线程可以并发执行,各自独立完成一些特定的任务,或者共同完成一个复杂的任务。进程里每个线程执行的顺序不一定。主线程结束,则所有线程结束。
    在这里插入图片描述

  2. 共享资源保护
       由于某一线程与同属一个进程的其他的线程共享进程的资源,如内存空间、文件描述符和其他一些进程相关的属性。所以在多线程编程中,通常会涉及到共享资源保护操作,如互斥锁等以确保线程之间的协调运行,避免资源竞争和数据不一致的问题。即每次对共享资源进行操作时,只能有一个线程操作,其他线程必须等待操作完毕后,才能对其继续操作。

二、线程的编译

   Linux 的线程是通过用户级的函数库实现的,一般采用 pthread 线程库实现线程的访问和控制。它用第 3 方 posix 标准的 pthread,具有良好的可移植性。在使用了线程的代码编译的时候要在后面加上 -lpthread

例如:gcc test.c -o test -lpthread

三、 线程相关函数

头文件:#include <pthread.h>

在这里插入图片描述

1. 线程的创建

int pthread_create(pthread_t* thread,  pthread_attr_t * attr, void *(*start_routine)(void *),  void * arg);
//pthread_t* thread :线程的句柄,用于区分是哪个线程。
//pthread_attr_t * attr :线程的属性,通常为NULL。
//void *(*start_routine)(void *) :线程所执行的函数,该函数形参和返回值都要为void *。
// void * arg :该值用于传递第三个参数函数的形参,如果不传递可以为NULL。

举例:
在这里插入图片描述

(1)整型的传入与接收

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *task(void *arg)
{int *num=(int *)arg;while(1){sleep(1); printf("我是子线程,传入参数为%d\n",*num);}
}int main()
{int num =100;pthread_t  thread; //定义线程句柄pthread_create(&thread, NULL, task, (void *)&num);  //创建线程,并绑定线程函数。  while(1){sleep(1); printf("我是主线程\n");}
}

在这里插入图片描述

(2)浮点数的传入与接收

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *task(void *arg)
{float*num=(float *)arg;while(1){sleep(1); printf("我是子线程,传入参数为%f\n",*num);}
}int main()
{float num =10.10;pthread_t  thread; //定义线程句柄pthread_create(&thread, NULL, task, (void *)&num);  //创建线程,并绑定线程函数。  while(1){sleep(1); printf("我是主线程\n");}
}

在这里插入图片描述

(3)字符串的传入与接收

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *task(void *arg)
{char *num=(char *)arg;while(1){sleep(1); printf("我是子线程,传入参数为%s\n", num);}
}int main()
{char *string="i love you!";pthread_t  thread; //定义线程句柄pthread_create(&thread, NULL, task, (void *)string);  //创建线程,并绑定线程函数。  while(1){sleep(1); printf("我是主线程\n");}
}

在这里插入图片描述

(4)结构体的传入与接收

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>struct people{char *name;int age;
};void *task(void *arg)
{struct people *student=(struct people*)arg;while(1){sleep(1); printf("我是子线程,传入参数名字:%s,年龄:%d\n", student->name,student->age);}
}int main()
{struct people *student;pthread_t  thread; //定义线程句柄//将结构体赋值student->age=10;strcpy(student->name, "John"); pthread_create(&thread, NULL, task, (void *)student);  //创建线程,并绑定线程函数。  while(1){sleep(1); printf("我是主线程\n");}
}

在这里插入图片描述

总结

   我们发现在创建线程时,给线程函数传递的参数都是取其变量的地址,这是因为该变量定义在进程中,它只有在程序结束时,才会被销毁,所以它一直存在,地址不会发送改变。
   但是线程的退出函数在传递参数时,如果变量在线程内定义,那么在返回值时就不能传其地址,因为线程在退出时,该值的地址也被销毁,那么接收线程退出时接收到的地址就是已经被销毁的地址。解决方法是将变量定义为全局变量或者使用static进行修饰,增加变量的生命周期,直至程序结束才被销毁。但是使用static修饰变量就会出现另一个问题,如果将一个局部变量改为static,所有调用该函数的线程将共享同一个变量。这会导致竞争条件和数据错乱,因为多个线程会同时读写同一个变量。如果多个线程需要独立的变量实例,应该要避免使用static修饰变量。

2. 线程的退出

   函数 pthread_exit 表示线程的退出。其传入的的参数可以被其它线程用 pthread_join 等待函数进行捕获。例如: pthread_exit( (void*)3 ); 表示线程退出,并将 3作为返回值被后面的 pthread_join 函数捕获。该函数的作用就是可以将该线程的计算结果传递给创建它的主线程或者其他线程。
   注意1:在使用线程函数时,不能随意使用 exit 退出函数进行出错处理,由于 exit 的作用是使调用进程终止,往往一个进程包括了多个线程,所以在线程中通常使用 pthread_exit 函数来代替进程中的退出函数 exit。
   注意2:如果线程退出的返回值是在线程内部定义的局部变量的话,记得加static修饰,增加变量的生命周期直至进程结束而结束。不然线程退出后,该片内存地址会立刻被销毁,此时返回的空间无效。或者将返回值修改为全局变量。

void pthread_exit(void *retval);
//void *retval:作为线程退出时的值。如果不需要捕获该值,可以传入NULL。

3. 线程的等待

   当父线程结束的时候,如果没有 pthread_join 函数等待子线程执行的话,父线程会退出,而主线程的退出会导致进程的退出,故子线程也会退出。
   由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放。正如进程之间可以通过 wait()函数系统调用来同步终止并释放资源一样,线程之间也有类似的机制,那就是 pthread_join 函数。这个函数是一个线程阻塞函数,调用这函数的线程将一直等待直到被等待的线程结束为止,当函数返回时,被等待线程的资源被回收。

int pthread_join(pthread_t pthid, void **thread_return);
//pthread_t pthid :线程句柄。
//void **thread_return : 用于接收线程退出的返回值。如果没有需要接收的填NULL。

   上述函数第二个参数为二级指针,所以我们可以定义一级指针再取地址来获取该值,然后再进行强制类型转换为(void **)。

举例:
在这里插入图片描述

在这里插入图片描述

思考:上述例程中为什么要用static来修饰变量呢?
   答:这是因为当线程退出时,其在内部定义的变量都会被销毁,可以将变量设为全局变量或者使用static来修饰变量,增加其生命周期,直至进程结束,才会被销毁。不然返回的地址将是一个已经被销毁的地址,无法使用。这是只有一个线程使用所以可以这么做,如果当多个线程公用同一个线程函数时,且需要独立的变量实例,应该要避免使用static修饰变量。

补充

   一般线程的退出和线程的等待是一起使用的。这里我们使用几种参数类型进行举例。

(1)返回整型

   使用long定义整型,这是因为long类型为8字节的长度,(void *)类型也为8字节长度,所以可以进行强制类型转换。如果字节大小不匹配则不可以进行强制类型转换。

//退出:
long a=10;
pthread_exit((void *) a);   //返回的是8字节的(void *)类型的数据值。
//-------------------------------//等待接收:
long thread_result;  //long 定义方便类型转换
pthread_join(thread,(void *)&thread_result); //因为返回数据是指针类型(void *),那么需要取地址并强制转为(void *)类型
printf("子线程的返回值为:%ld\n", (long)thread_result);

(2)返回浮点数

   使用double 定义浮点型,这是因为double 类型为8字节的长度,(void *)类型也为8字节长度,所以可以进行强制类型转换。如果字节大小不匹配则不可以进行强制类型转换。

//退出:
double f=10.12;
pthread_exit((void *) f);
//-------------------------------
//等待接收:
double thread_result;
pthread_join(thread,(void *)&thread_result);
printf("子线程的返回值为:%f\n", (double)thread_result);

(3)返回字符串

//退出:
char *string="i love you!";
pthread_exit((void *)string);
//-------------------------------
//等待接收:
char *thread_result;
pthread_join(thread,(void **)&thread_result);
printf("子线程的返回值为:%s\n", thread_result);

(4)返回结构体

typedef struct 
{char *name;  //定义指针,后续使用时要为其开辟内存空间! 如果不想开辟空间,则使用数组。int  age;
}people;//退出:people *student = (people *)malloc(sizeof(people)); //给结构体开辟空间,然后赋值。student->name=(char *)malloc(sizeof(char *)); //为结构体里定义的字符指针开辟内存空间。//给结构体赋值strcpy(student->name,"john");student->age=12;pthread_exit((void *)student);//等待接收student *thread_result;pthread_join(thread,(void **)&thread_result);printf("名字为:%s,年龄为:%d\n", thread_result->name,thread_result->age);

四、综合举例

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void *task1(void * arg)    //线程1函数
{float *num =(float *)arg;while(1){sleep(1);printf("这是子线程1,传入的值为%f\n",*num);}  
}void *task2(void * arg)    //线程2函数
{int *num =(int *)arg;static char *string="pthread2_end";while(1){  sleep(1);printf("这是子线程2,传入的值为%d\n",*num);(*num)--;if(*num <0) break;}pthread_exit((void *)string);
}int main()
{float pthread1_arg=11.2;  //传入线程函数1的参数int pthread2_arg=3;  //传入线程函数2的参数char *string;int ret;pthread_t thread1,thread2;//线程句柄//创建线程1ret=pthread_create(&thread1, NULL, task1, (void *) &pthread1_arg);if(ret<0) {perror("pthread1_create error!\n");return -1;}//创建线程2ret=pthread_create(&thread2, NULL, task2, (void *) &pthread2_arg);if(ret<0) {perror("pthread2_create error!\n");return -1;}pthread_join(thread2, (void **)&string);printf("%s\n",string);return 0;
}

在这里插入图片描述

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

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

相关文章

Django 解决 CSRF 问题

在 Django 出现 CSRF 问题 要解决这个问题&#xff0c;就得在 html 里这么修改 <!DOCTYPE html> <html><head></head><body><form action"/login/" method"post">{% csrf_token %}</form></body> </…

持续领跑教育科技,网易有道再发“子曰”教育大模型全新应用

5月29日&#xff0c;网易有道“子曰”教育大模型媒体交流会在北京举行。会上&#xff0c;网易有道分享了子曰教育大模型最新技术进展及三大AI创新应用&#xff1a;AI全科学习助手“有道小P”APP、新一代虚拟人口语教练Hi Echo 3.0和新一代知识库问答引擎QAnything。 现场&…

debian11安装留档@VirtualBox

因为debian12无法安装tpot&#xff0c;所以又把11重新安装一遍&#xff0c;以前的安装文档&#xff1a;安装Debian 11 留档-CSDN博客 下载光盘 华为云地址&#xff1a;https://repo.huaweicloud.com/debian-cd/11.0.0/amd64/iso-cd/ 使用了debian11 教育版&#xff0c;比较有…

[Linux系统编程] 静态库与动态库

一.库的概念 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复用的代码。 本质上来说库是一种可执行代码的二进制形式&#xff0c;可以被操作系统载入内存执行。库有两种&#xff1a;静态库&#xff08;.a、.lib&#xff09;和动态库&#xff08;.so、.dll&#xff09;。…

webpack快速入门---webpack的安装和基本使用

webpack是什么 本质上&#xff0c;webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时&#xff0c;它会在内部从一个或多个入口点构建一个 依赖图(dependency graph)&#xff0c;然后将你项目中所需的每一个模块组合成一个或多个 bund…

集合的综合练习

自动点名器1&#xff1a;班级里有N个学生&#xff0c;实现随机点名器 public class test {public static void main(String [] args) {ArrayList<String> listnew ArrayList<>();//创建一个集合//在集合中添加元素Collections.addAll(list, "李明",&quo…

cf练习5.29

A ac代码 #include<bits/stdc.h> #define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);using namespace std;int main() {IOSint t;cin>>t;while(t--){int ans0;int a,b;cin>>a>>b;if(b0){if(a%150) ansa/15;else ansa/151;}else if(b!0…

基于python实现生命游戏

文章目录 一、生命游戏是什么二、生命游戏规则解释1.相邻细胞2.细胞状态 三、代码实现1.邻居细胞2.更新状态 四、整体代码 一、生命游戏是什么 生命游戏&#xff08;Game of Life&#xff09;是由英国数学家约翰何顿康威在1970年发明的一种细胞自动机&#xff08;Cellular Aut…

基于深度学习的中文情感分析系统python flask

基于python的毕业设计 基于深度学习的中文情感分析系统(flask)(源码说明文档演示) 毕业设计课程设计期末大作业、课程设计、高分必看&#xff0c;下载下来&#xff0c;简单部署&#xff0c;就可以使用。 包含&#xff1a;项目源码、数据库脚本、软件工具等&#xff0c;该项目…

NVR对接三方相机预览黑屏问题案例

一、 问题现象 【问题现象】NVR接入三方相机,通道状态显示在线,但本地、web预览显示黑屏。更换H.264&#xff0c;H.265均预览黑屏&#xff0c;且NVR侧的萤石云手机APP预览报错260025。 【现场拓扑】现场拓扑如下 &#xff08;1&#xff09; IPC使用onvif协议添加至NVR&#xff…

全局配置-案例:配置tabBar

一、需求&#xff1a;实现如图所示的tabBar效果 二、实现步骤&#xff1a; 1.拷贝图标资源 把image文件夹拷贝到小程序项目根目录中 图片中包含-active的是选中之后的图标 图片中不包含-active的是默认图标 2.新建3个对应的tab页面 3.配置tabBar选项 &#xff08;1&#xf…

【本地运行chatgpt-web】启动前端项目和service服务端项目,也是使用nodejs进行开发的。两个都运行成功才可以使用!

1&#xff0c;启动web界面 https://github.com/Chanzhaoyu/chatgpt-web#node https://nodejs.org/en/download/package-manager # 使用nvm 安装最新的 20 版本。 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source /root/.bashrc n…

MVS net笔记和理解

文章目录 传统的方法有什么缺陷吗&#xff1f;MVSnet深度的预估 传统的方法有什么缺陷吗&#xff1f; 传统的mvs算法它对图像的光照要求相对较高&#xff0c;但是在实际中要保证照片的光照效果很好是很难的。所以传统算法对镜面反射&#xff0c;白墙这种的重建效果就比较差。 …

SSE(Server Sent Event) 踩坑留念

整条链路是 客户端A --> 服务端 A —> 服务端 B 我负责服务端 A 此时要注意 Client 中的 processes 的写法 Post(value “/v2/xx”, processes MediaType.TEXT_EVENT_STREAM) 这样写是一直报错的 改成下面的写法才可以 Post(value “/v2/xx”, processes MediaT…

看看最新的B端登录界面,你是不是被潮流抛弃了?

毛玻璃风格&#xff08;Frosted Glass Style&#xff09;是新拟态设计风格中的一种分支&#xff0c;它灵感来源于现实世界中的毛玻璃材质。毛玻璃是一种通过在玻璃表面加工处理的方式&#xff0c;使其具有模糊、云翳和透明效果的特殊玻璃。 在设计中&#xff0c;毛玻璃风格通常…

WPF中CommandParameter用法

1. 界面样式 2. XAML中代码部分 <ButtonGrid.Row"0"Grid.Column"1"Command"{Binding BtnClick_Number}"CommandParameter"7"Content"7"Style"{StaticResource BtnStyle_Num}" /> <ButtonGrid.Row"…

华发股份:加强业务协同 新政下项目热销

“5.17”楼市政策出台后&#xff0c;各地密集落地执行。5月27—28日&#xff0c;上海、广州、深圳三个一线城市跟进落地“517”新政。上海发布《关于优化本市房地产市场平稳健康发展政策措施的通知》&#xff0c;共计9条调整政策&#xff0c;涵盖外地户籍、人才、单身、婚否、企…

Python数据处理,使用 tkinter 模块点击获取文件目录

Python数据处理&#xff0c;使用 tkinter 模块点击获取文件目录 正文 正文 当我们进行数据处理读取文件内数据的时候&#xff0c;通常&#xff0c;我们需要设定好一个存放当前文件所在目录的变量。比如如下目录&#xff1a; file_path rC:\Users\xxx\Desktop\DataSet\Data.c…

【OrangePi AIpro】开箱初体验以及OAK深度相机测试

1. 简介 Orangepi AIPRO 是一款采用昇腾AI技术路线&#xff0c;集成4核64位处理器AI处理器的单板计算机&#xff0c;集成图形处理器&#xff0c;支持8TOPS AI算力&#xff0c;拥有8GB/16GB LPDDR4X&#xff0c;可以外接eMMC模块&#xff0c;支持双4K高清输出。 Orange Pi AIpr…

Docker-一文详解容器通信的基础网络模式及衍生的自定义网络模式

启动容器时&#xff0c;通过-p 宿主机端口:容器端口&#xff0c;就可以通过访问宿主机端口访问到容器&#xff0c;这种原理机制是啥&#xff0c;有没有其它方式可以让宿主机和容器通信&#xff0c;以及容器与容器之间如何通信。带着这几个问题开始学习Docker的网络知识。 文章…