当前位置: 首页 > news >正文

Linux系统编程(五)多线程

目录

  • 一、基本知识点
  • 二、线程的编译
  • 三、 线程相关函数
    • 1. 线程的创建
    • 2. 线程的退出
    • 3. 线程的等待
    • 补充
  • 四、综合举例

  

一、基本知识点

   线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个标准的线程由线程ID、当前指令指针(PC)、寄存器集合和堆栈组成。它与同属一个进程的其他的线程共享进程的资源,如内存空间、文件描述符和其他一些进程相关的属性。
   相比于进程而言,线程更加轻量级,因为它们共享了进程的地址空间以及其他资源,所以线程之间的切换和通信会更加高效。一个进程可以包含多个线程,这些线程可以并发执行,各自独立完成一些特定的任务,或者共同完成一个复杂的任务。主线程结束,则所有线程结束。
在这里插入图片描述

   在多线程编程中,通常会涉及到线程的创建、同步、互斥、通信等操作,以确保线程之间的协调运行,避免资源竞争和数据不一致的问题。

二、线程的编译

   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。

2. 线程的退出

   函数 pthread_exit 表示线程的退出。其传入的的参数可以被其它线程用 pthread_join 等待函数进行捕获。例如: pthread_exit( (void*)3 ); 表示线程退出,并将 3作为返回值被后面的 pthread_join 函数捕获。该函数的作用就是可以将该线程的计算结果传递给创建它的主线程或者其他线程。
   注意:在使用线程函数时,不能随意使用 exit 退出函数进行出错处理,由于 exit 的作用是使调用进程终止,往往一个进程包括了多个线程,所以在线程中通常使用 pthread_exit 函数来代替进程中的退出函数 exit。

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。

补充

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

int a=10;
pthread_exit((void *) &a);

(2)返回浮点数

int f=10.12;
pthread_exit((void *) &f);

(3)返回字符串

char *string="i love you!";
pthread_exit(string);

(4)返回结构体

typedef struct 
{char name[10];int  number;float score;
}student;
//方式一
student *people;
pthread_exit(people);
//方式二
student people;
pthread_exit((void *)&people);

举例:

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void *func1 (void *arg)
{int n=5;  float *p=(float *)arg; while(1){ if(n<=0) break; printf("我是子线程,参数为:%f\n", *p);n--;sleep(1);} pthread_exit( (void*)123 ); 	
}  int main(int argc, char **argv)
{int ret;int thread_result;     float f_number=0.255;  pthread_t thread1;  //线程句柄//创建线程:ret=pthread_create(&thread1, NULL, func1, (void *)&f_number); //最后一个参数要取地址!if(ret!=0){perror("pthread_create error!\n");return -1;}//等待子线程退出,并释放其占用的资源pthread_join(thread1, (void **)&thread_result);printf("thread_result:%d\n", thread_result);
}   

在这里插入图片描述

四、综合举例

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void *fun1(void * arg)    //线程1函数
{float *num =(float *)arg;while(1){sleep(1);printf("这是子线程1,传入的值为%f\n",*num);}  
}void *fun2(void * arg)    //线程2函数
{int *num =(int *)arg;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的参数int ret;pthread_t thread1,thread2;//线程句柄//创建线程1ret=pthread_create(&thread1, NULL, fun1, (void *) &pthread1_arg);if(ret<0) {perror("pthread1_create error!\n");return -1;}//创建线程2ret=pthread_create(&thread2, NULL, fun2, (void *) &pthread2_arg);if(ret<0) {perror("pthread2_create error!\n");return -1;}while(1){sleep(1);printf("我是主线程\n");}return 0;
}

在这里插入图片描述

相关文章:

  • 解决鼠标滚动时element-ui下拉框错位的问题
  • MarkDown语法使用手册
  • huggingface的self.state与self.control来源(TrainerState与TrainerControl)
  • 现代操作系统上创建各类链接的方法汇总
  • C语言函数复习全解析:参数、无参、嵌套与递归
  • 【Docker】2、配置SSL证书远程访问Docker
  • Golang面试手册
  • springboot项目使用validated参数校验框架
  • 为什么国际顶级黑客大都没学过计算机专业,而是自学成才?
  • Docker安装nginx详细教程
  • React 使用JSX或者TSX渲染页面
  • LLAMA3==shenzhi-wang/Llama3-8B-Chinese-Chat。windows安装不使用ollama
  • day21二叉树part07|530.二叉搜索树的最小绝对差 501.二叉搜索树中的众数 236. 二叉树的最近公共祖先
  • 【网络运维的重要性】
  • 学习C++编程入门:时间、方法及经验分享
  • 分享一款快速APP功能测试工具
  • 【5+】跨webview多页面 触发事件(二)
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • canvas 绘制双线技巧
  • Java 多线程编程之:notify 和 wait 用法
  • Java|序列化异常StreamCorruptedException的解决方法
  • Javascript设计模式学习之Observer(观察者)模式
  • KMP算法及优化
  • spring cloud gateway 源码解析(4)跨域问题处理
  • 看域名解析域名安全对SEO的影响
  • 快速构建spring-cloud+sleuth+rabbit+ zipkin+es+kibana+grafana日志跟踪平台
  • 力扣(LeetCode)965
  • 排序算法之--选择排序
  • 前端知识点整理(待续)
  • 如何设计一个微型分布式架构?
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • Java数据解析之JSON
  • MyCAT水平分库
  • 整理一些计算机基础知识!
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • ​queue --- 一个同步的队列类​
  • #控制台大学课堂点名问题_课堂随机点名
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • (第一天)包装对象、作用域、创建对象
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (十一)c52学习之旅-动态数码管
  • (五)Python 垃圾回收机制
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • (转)母版页和相对路径
  • . ./ bash dash source 这五种执行shell脚本方式 区别
  • .gitignore文件设置了忽略但不生效
  • .NET Core WebAPI中封装Swagger配置
  • .NET Core跨平台微服务学习资源
  • .Net IE10 _doPostBack 未定义
  • .NET 材料检测系统崩溃分析
  • .NET 设计模式—简单工厂(Simple Factory Pattern)
  • .net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池
  • .NET版Word处理控件Aspose.words功能演示:在ASP.NET MVC中创建MS Word编辑器
  • .net打印*三角形
  • .secret勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复