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");
   }
}

在这里插入图片描述

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来修饰变量,增加其生命周期,直至进程结束,才会被销毁。不然返回的地址将是一个已经被销毁的地址,无法使用。

补充

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

(1)返回整型

//退出:
static int a=10;
pthread_exit((void *) &a);
//-------------------------------

//等待接收:
int *thread_result;
pthread_join(thread,(void **)&thread_result);
printf("子线程的返回值为:%d\n", *thread_result);

(2)返回浮点数

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

(3)返回字符串

//退出:
static 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;//线程句柄
	
	//创建线程1
	ret=pthread_create(&thread1, NULL, task1, (void *) &pthread1_arg);
	if(ret<0) 
	{
	   perror("pthread1_create error!\n");
	   return -1;
	}
	
	//创建线程2
	ret=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;
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智慧仓储新动力:EasyCVR+AI视频智能监管系统方案助力仓储安全高效管理

一、背景 随着物流行业的快速发展和智能化水平的提升&#xff0c;智慧仓储视频智能监管系统已成为现代仓储管理的重要组成部分。本系统通过综合运用物联网、视频分析、边缘计算等技术手段&#xff0c;实现对仓储环境的全面监控、智能分析和高效管理。 TSINGSEE青犀视频汇聚Ea…

【单片机毕设选题】-智能语音控制风扇

一. 系统功能 此设计采用STM32和SU-03T离线语音模块来控制风扇, 主要功能如下: 1. 通过DHT11温湿度模块来采集环境温湿度。 2. 通过SU-03T语音模块来控制风扇启停加减速等。 3. 通过OLED显示系统状态。 4. 可以通过按键控制风扇启停加减速等。 5. 通过蓝牙模块监视系统状…

【IOT】OrangePi+HomeAssistant+Yolov5智能家居融合

前言 本文将以OrangePi AIpro为基础&#xff0c;在此基础构建HomeAssistant、YOLO目标检测实现智能家居更加灵活智能的场景实现。 表头表头设备OrangePi AIpro(8T)系统版本Ubuntu 22.04.4 LTSCPU4核64位处理器 AI处理器AI算力AI算力 8TOPS算力接口HDMI2、GPIO接口、Type-C、M.2…

06.逻辑回归

文章目录 Generate Model优化边界为线性证明损失函数比较逻辑回归不能用均方误差Generative v.s. DiscriminativeMulti-class Classification逻辑回归的限制自己找线性变换 Generate Model 假设样本符合高斯分布 即找 μ \mu μ和 σ \sigma σ 优化 共用 Σ \Sigma Σ减少…

CSS学习笔记:vw、vh实现移动端适配

移动端适配 移动端即手机端&#xff0c;也称M端 移动端适配&#xff1a;同一套移动端页面在不同屏幕尺寸的手机上可以实现宽度和高度的自适应&#xff0c;也就是页面中元素的宽度和高度可以根据屏幕尺寸的变化等比缩放 之前我在一篇博客中介绍了rem实现移动端适配&#xff0…

这样写代码太优雅了吧

文章目录 优化案例初次优化再次优化看看Spring源码的处理 优化案例 假设一个场景&#xff0c; 开发代码时&#xff0c;需要对类中的方法进行遍历&#xff0c;判断有没有注解NotNull&#xff0c;暂时没有合适的工具类&#xff0c;需要自己手搓一个。 无须多想&#xff0c;分分钟…

CSS学习笔记:响应式布局的原理——媒体查询

什么是响应式布局&#xff1f; 在实际书写代码时&#xff0c;我们不会自己去手写媒体查询来实现响应式布局&#xff0c;我们一般会调用现成的代码库或使用现成的框架&#xff08;但这些代码库或框架的底层原理是媒体查询&#xff0c;所以了解媒体查询也是很有必要的&#xff0…

应用弹窗优先级

背景 由于活动业务越来越多&#xff0c;积累的弹窗越来越多和杂乱&#xff0c;出现如下弹窗交互问题&#xff1a; 弹窗无限重叠&#xff0c;影响操作 弹出顺序无优先级&#xff0c;重要弹窗被隐藏 原因相信大家都一样&#xff0c;产品是一次次迭代的&#xff0c;也可能是不…

ozon卖家精灵,ozon卖家怎么使用

在跨境电商的浪潮中&#xff0c;OZON作为俄罗斯领先的电商平台&#xff0c;吸引了众多卖家争相入驻。然而&#xff0c;面对日益激烈的市场竞争&#xff0c;如何提升店铺的运营效果&#xff0c;成为卖家们迫切需要解决的问题。而OZON卖家精灵作为一款专为OZON卖家打造的辅助工具…

Lua的几个特殊用法

&#xff1a;/.的区别 详细可以参考https://zhuanlan.zhihu.com/p/651619116。最重要的不同就是传递默认参数self。 通过.调用函数&#xff0c;传递self实例 通过 &#xff1a; 调用函数&#xff0c;传递self (不需要显示的传递self参数&#xff0c;默认就会传递&#xff0c;但…

ROS | C++和python实现发布结点和订阅结点

发布者结点&#xff1a; 代码实现&#xff1a; python: C: C和Python发布结点的差异&#xff1a; python: 发布结点pub 大管家rospy调用publisher函数发布话题 (话题,类型&#xff0c;话题长度&#xff09; C: 先定义一个大管家&#xff1a;NodeHandle 然后大管家发布话题…

【C++】二维前缀和

1.题目 2.算法思路 和一维前缀和的方法类似&#xff0c;我们需要预处理一个求和矩阵&#xff0c;然后再求和。 下面是模板&#xff1a; 上面两张图片总结出来了两个公式&#xff0c;这是解决此类问题的关键。 3.代码 #include <iostream> using namespace std; #incl…

【车载开发系列】Vector工具链的安装

【车载开发系列】Vector工具链的安装 【车载开发系列】Vector工具链的安装 【车载开发系列】Vector工具链的安装一. VectorDriver二. DaVinci_Developer三. DaVinci Configurator 一. VectorDriver Vector Driver Setup是Vector产品链中重要的驱动软件,所有的硬件设备进行连接…

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

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

PS:电子书App自动截图后合成一个PDF文档

说明&#xff1a;有的电子书App不能下载到本地&#xff0c;通过自动截图后合成一个PDF文档来解决&#xff01; 一、自动截图App 1.安装”免ROOT自动化助手“ 2.创建一个任务 3.编辑任务&#xff1a;根据电子书的操作顺序制定&#xff0c;400次就是书籍页数&#xff08;次数一…

备份服务器的安全风险以及如何通过TDE透明加密提升安全性

备份服务器的潜在安全风险主要包括以下几个方面&#xff1a; 1. 数据泄露风险&#xff1a; 备份数据可能包含敏感信息&#xff0c;如用户个人信息、商业机密等。如果备份数据未经适当保护&#xff0c;例如存储在不安全的位置或未加密&#xff0c;黑客或未授权的人员可能会获取…

React-基础样式控制

组件基础样式方案 React组件基础的样式控制有两种方式 1、行内样式&#xff08;不推荐&#xff09; 属性名是多个单词的需要使用驼峰写法 也可以把样式都提取到一个变量里&#xff0c;再赋值到style里 2、class类名控制 classnames优化类名控制 classnames是一个简单的JS库&…

【揭秘!在线ChatGPT神器,体验入口在此!】

&#x1f680;【揭秘&#xff01;在线ChatGPT神器&#xff0c;体验入口在此&#xff01;】&#x1f680; 前言 嘿&#xff0c;大家好&#xff01;今天我要和大家分享一些关于如何使用免费的ChatGPT的技巧。ChatGPT是一项令人兴奋的人工智能技术&#xff0c;它可以成为我们的好…

沃飞长空总部落地成都高新,为蓉低空经济发展助力!

5月25日&#xff0c;吉利科技集团与成都高新区签署合作协议&#xff0c;吉利科技集团旗下沃飞长空全球总部落地成都高新区。 根据协议&#xff0c;沃飞长空全球总部项目落地成都未来科技城&#xff0c;将布局总部办公、研发和生产制造低空出行航空器等业务。双方将积极发挥各自…

MySQL第六次作业

一、创建部门表 指令&#xff1a; mysql> CREATE TABLE dept (-> dept_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 部门编号,-> dept_name CHAR(20) COMMENT 部门名称-> ); 演示&#xff1a; 二、插入部门数据 指令&#xff1a; mysql> INSERT INTO dept…