1.线程概述

程序运行起来编程进程,进程由一个个线程构成。

eg:

没有启动的qq时一个程序,启动后登录qq,qq是一个进程,实际上进程什么都没做,只是提供了需要的资源,打开聊天框可以和别人进行通信,这就是线程,和多个人聊天就是多线程。

每个进程中至少包含一个主线程。

线程是轻量级的进程(LWP:light weight process),再linux环境下的本质仍然是基础。

进程是最小的资源分配单位,线程是最小的执行单位。

2.线程资源

线程只共享:

  • 文件描述符
  • 每种信号的处理方式
  • 当前工作目录
  • 用户ID和组ID,内存地址空间

独占的资源:(栈区不共享)

  • 线程ID
  • 处理器现场和栈指针(内核栈)
  • 独立的栈空间(用户空间栈)
  • errno变量
  • 信号屏蔽字
  • 调度优先级

3.线程的优点和缺点

优点:

  • 提高程序并发性
  • 开销小
  • 数据通信、共享数据方便

缺点:

  • 库函数,不稳定
  • 调试、编写困难、gdb不支持
  • 对信号支持不好

4.线程的创建

4.1 线程号

线程号在对应的进程里是唯一的。

进程号:pid_t 非负整数

线程号:pthread_t 无符号长整型

4.2 API

4.2.1 pthread_self()

#include <pthread.h>

pthread_t pthread_self(void);

函数功能:

  • 获取当前线程的线程号

返回值:

  • 获取的线程号

后续如果遇到编译不通过,提示pthread相关,可以编译的时候引入相关资源。 -pthread

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>int main(int argc, char const *argv[])
{pid_t pid=getpid();//获取进程号pthread_t pt1=pthread_self();//获取当前进程的主线程号printf("pid:%u,%lu\n",pid,pt1);  return 0;
}

4.2.2 pthread_create()

#include <pthread.h>

int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg );

函数功能:

  • 创建一个线程

参数:

  • thread:线程标识符地址。
  • attr:线程属性结构体地址,通常设置为 NULL。
  • start_routine:线程函数的入口地址。函数指针。
  • arg:传给线程函数的参数。

返回值:

  • 成功:0
  • 失败:非 0

线程函数不传参:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>void *func(void *argv)
{printf("线程%lu正在运行\n", pthread_self());
}int main(int argc, char const *argv[])
{pid_t pid = getpid();           // 获取进程号pthread_t pt1 = pthread_self(); // 获取当前进程的主线程号printf("进程:%u,主线程:%lu\n", pid, pt1);pthread_t pt2;int ret = pthread_create(&pt2, NULL, func, NULL); // 创建线程if (ret != 0){perror("pthread_create");return 0;}// 在此处阻塞,避免线程未执行,进程就结束,进程结束,系统会回收进程资源// 线程之间是并发的,而不是简单的从上往下执行sleep(1);return 0;
}

线程函数传参:数组

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>void *func(void *argv)
{printf("线程:%lu 内容:%s\n", pthread_self(), (char *)argv);// 此处char*不能省,因为cpu访问内存读取数据,必须要约束类型
}int main(int argc, char const *argv[])
{pid_t pid = getpid();           // 获取进程号pthread_t pt1 = pthread_self(); // 获取当前进程的主线程号printf("进程:%u,主线程:%lu\n", pid, pt1);char buff[10] = "hello";pthread_t pt2;int ret = pthread_create(&pt2, NULL, func, buff); // 创建线程// int ret =pthread_create(&pt1,NULL,func,(void *)buff);// 以上两种都可以,因为地址本身没有类型,char *  void*都可以,只是地址而已if (ret != 0){perror("pthread_create");return 0;}// 在此处阻塞,避免线程未执行,进程就结束,进程结束,系统会回收进程资源// 线程之间是并发的,而不是简单的从上往下执行sleep(1);return 0;
}

线程函数传参:整数

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>void *func(void *argv)
{*(int *)argv = 20;
}int main(int argc, char const *argv[])
{pthread_t pt1 = pthread_self();int num = 10;int ret = pthread_create(&pt1, NULL, func, &num);// int ret =pthread_create(&pt1,NULL,func,(void *)num);// 以上两种都可以,因为地址本身没有类型,int *  void*都可以,只是地址而已if (ret != 0){perror("pthread_create");return 0;}sleep(1);printf("num=%d\n", num);return 0;
}

4.2.3 多线程创建

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
pthread_t pt1, pt2;
void *func1(void *argv)
{int count = 1;while (1){printf("线程:%lu:%s,执行了%ds\n", pt1, (char *)argv, count);count++;if (count == 5){break;}}
}
void *func2(void *argv)
{int count = 0;while (1){printf("线程:%lu:%s,执行了%ds\n", pt2, (char *)argv, count);count++;if (count == 5){break;}}
}int main(int argc, char const *argv[])
{pt1 = pthread_self();pt2 = pthread_self();int ret1 = pthread_create(&pt1, NULL, func1, NULL);if (ret1 != 0){perror("pthread_create");return 0;}int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_create");return 0;}sleep(1);return 0;
}

4.3.4 pthread_join()

#include <pthread.h>

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

函数功能:

  • 等待线程结束(此函数会阻塞),并回收线程资源,类似进程的 wait() 函数。如果线程已经结束,那么该函数会立即返回。

参数:

  • thread:被等待的线程号。
  • retval:用来存储线程退出状态的指针的地址,二级指针

返回值:

  • 成功:0
  • 失败:非 0
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
pthread_t pt1, pt2;
void *func1(void *argv)
{int count = 1;while (1){printf("线程:%lu:%s,执行了%ds\n", pt1, (char *)argv, count);count++;if (count == 5){break;}}return "func1";
}
void *func2(void *argv)
{int count = 0;while (1){printf("线程:%lu:%s,执行了%ds\n", pt2, (char *)argv, count);count++;if (count == 5){break;}}
}int main(int argc, char const *argv[])
{pt1 = pthread_self();pt2 = pthread_self();int ret1 = pthread_create(&pt1, NULL, func1, NULL);if (ret1 != 0){perror("pthread_create");return 0;}int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_create");return 0;}void *returnvalue;printf("returnvalue:%p\n",returnvalue);pthread_join(pt1,&returnvalue);//pt1 返回地址是  "func1" 这个字符串的首地址printf("pt1退出状态:%s\n",(char *)returnvalue);return 0;
}

栈区数据不会共享,返回栈区数据导致段错误

函数返回栈区数据段错误示例
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
pthread_t pt1, pt2;void *func2(void *argv)
{int num=10;int count = 0;while (1){printf("线程:%lu:%s,执行了%ds\n", pt2, (char *)argv, count);count++;if (count == 5){break;}}return (void *)&num;
}int main(int argc, char const *argv[])
{pt2 = pthread_self();int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_create");return 0;}void *returnvalue;printf("returnvalue:%p\n",returnvalue);pthread_join(pt2,&returnvalue);//pt2 返回地址是  "func2" 这个字符串的首地址printf("pt2退出状态:%s\n",(char *)returnvalue);return 0;
}

返回堆区地址:

#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>void *func1(void *argv);
void *func2(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1, pt2;int ret1 = pthread_create(&pt1, NULL, func1, NULL);if (ret1 != 0){perror("pthread_creat()");}int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_creat()");}void *returnvalue;void *returnvalue2;pthread_join(pt1, &returnvalue); // pt1 返回地址是  "pthread_func1" 这个字符串的首地址pthread_join(pt2, &returnvalue2); // printf("pt1退出状态取值:%s\n", (char *)returnvalue); //强转,取字符串 结果 "pthread_func1"printf("pt2退出的地址取值:%d\n",*(int *)returnvalue2);return 0;
}void *func1(void *argv)
{int count = 1;while (1){printf("当前所在进程%u,当前线程%lu,执行了%dS\n", getpid(), pthread_self(), count);sleep(1);count++;if (count >= 6){break;}}return "pthread_func1";
}void *func2(void *argv)
{int *p = (int *)malloc(sizeof(int));*p = 10;int count = 1;while (1){printf("当前所在进程%u,当前线程%lu,执行了%dS\n", getpid(), pthread_self(), count);sleep(1);count++;if (count >= 6){break;}} return p; //不能是栈区地址,因为栈区地址不共享,每一个线程都有自己的栈
}

4.3.5pthread_detach()

一般情况下,线程终止后,其终止状态一直保留到其它线程调用 pthread_join 获取它的状态为止。但是线程也可以被置为 detach 状态,这样的线程一旦终止就立刻回收(系统回收)它占用的所有资源,而不保留终止状态。

所以如果回收线程资源: pthread_join与pthread_detach只能二选一

 #include <pthread.h>

int pthread_detach(pthread_t thread);

函数功能:

  • 将指定线程标记为分离状态。

参数:

  • pthread :指定的要分离的线程

4.3.6 pthread_exit()

#include <pthread.h>

void pthread_exit(void *retval);

函数功能:

  • 退出调用线程。一个进程中的多个线程是共享该进程的数据段,因此,通常线程退出后所占用的资源并不会释放。

参数:

  • retval:存储线程退出状态的指针。
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#define sucess 0
#define error 1void *pthread_func1(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1;char buff[] = "hello my thread";int ret = pthread_create(&pt1, NULL, pthread_func1, buff); // 创建线程,线程函数传参printf("pt1=%lu\n", pt1);if (ret != 0){perror("pthread_create())");return 0;}printf("在进程%u中,主线程号:%lu\n", getpid(), pthread_self());void *return_value;pthread_join(pt1, &return_value);if (return_value == NULL) // 0{printf("success\n");}else{printf("error\n"); // 1}// free(return_value);return 0;
}void *pthread_func1(void *argv)
{for (int i = 1; i <= 5; i++){printf("线程%lu,执行了%dS\n", pthread_self(), i);sleep(1);if (i == 5){pthread_exit((void *)sucess); // void * + 数值,直接转换为地址}}return NULL;
}

4.3.7 pthread_cancel()

#include <pthread.h>

int pthread_cancel(pthread_t thread);

函数功能:

  • 杀死(取消)线程

参数:

  • thread : 目标线程 ID。

返回值:

  • 成功:0
  • 失败:出错编号

线程的取消并不是实时的,而有一定的延时。

需要等待线程到达某个取消点。 

取消点:执行命令 man 7 pthreads 可以查看具备这些取消点的系统调用列表。 可粗略认为一个系统调用(进入内核)即为一个取消点,例如sleep();

#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>void *myfunc1(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1;int ret = pthread_create(&pt1,NULL,myfunc1,NULL);pthread_cancel(pt1);while (1){/* code */}return 0;
}void *myfunc1(void *argv)
{while (1){printf("当前所在进程%u,当前线程%lu\n",getpid(),pthread_self());sleep(1);printf("check\n"); //cancel完不会执行,因为上面有取消点}}

5.线程的属性

typedef struct
{
int etachstate; //线程的分离状态
int  schedpolicy; //线程调度策略
struct sched_param schedparam; //线程的调度参数
int  inheritsched; //线程的继承性
int  scope; //线程的作用域
size_t guardsize; //线程栈末尾的警戒缓冲区大小
int  stackaddr_set; //线程的栈设置
void* stackaddr; //线程栈的位置
size_t stacksize; //线程栈的大小
} pthread_attr_t;

提前设置分离属性

  • 先进行属性初始化
  • 设置某一个属性
  • 创建线程
  • 销毁线程属性

5.1 pthread_attr_init()

int pthread_attr_init(pthread_attr_t *attr);

函数功能:

  • 对线程属性结构体 attr初始化

参数:

  • attr 要初始化的结构体

5.2 pthread_attr_setdetachstate()

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

函数功能:

  • 线程分离属性设置

参数:

  • attr:已初始化的线程属性
  • detachstate:分离状态

PTHREAD_CREATE_DETACHED(分离线程)

PTHREAD_CREATE_JOINABLE(非分离线程)

5.3 

int pthread_attr_destroy(pthreadattrt *attr);

函数功能:

  • 线程销毁属性设置

参数:

  • attr:要销毁的线程属性

函数返回值:

  • 成功:0;
  • 失败:错误号
#define _POSIX_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>void *myfunc1(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1;pthread_attr_t attr;// 初始化pthread_attr_init(&attr);// 设置分离属性pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);// 创建线程int ret = pthread_create(&pt1, &attr, myfunc1, NULL);// destorypthread_attr_destroy(&attr);while (1){/* code */}return 0;
}void *myfunc1(void *argv)
{while (1){printf("当前所在进程%u,当前线程%lu\n", getpid(), pthread_self());sleep(1);}
}

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

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

相关文章

2.10组件间的通信

1.Vue组件的嵌套关系1.1认识组件的嵌套前面我们是将所有的逻辑放到一个 App.vue 中&#xff1b;在之前的案例中&#xff0c;我们只是创建了一个组件 App&#xff1b;如果我们一个应用程序所有的逻辑都放在一个组件中&#xff0c;那么这个组件就会变成非常的臃肿和难以维护&…

Mybatis-Plus学习笔记

目录 一、MyBatis-Plus简介 二、MyBatisPlus使用的基本流程&#xff1a; &#xff08;1&#xff09;引入MybatisPlus依赖&#xff0c;代替MyBatis依赖 &#xff08;2&#xff09;自定义Mapper继承BaseMapper ​编辑&#xff08;3&#xff09;在实体类上添加注解声明表信息…

Day22 用C语言编译应用程序

文章目录1. 保护操作系统5&#xff08;harib19a&#xff09;2. 帮助发现bug&#xff08;harib19b&#xff09;3. 强制结束应用程序&#xff08;harib19c&#xff09;4. 用C语言显示字符串&#xff08;harib19e&#xff09;5. 显示窗口&#xff08;harib19f&#xff09;1. 保护操…

简单学习HTML+CSS+JavaScript

一、HTML HTML被称为 超文本标记语言&#xff0c;是由一系列标签构成的语言。 下面介绍HTML中的标签&#xff1a; &#xff08;一&#xff09;HTML文件基本结构 <!DOCTYPE html><html><head><title>Document</title></head> <body&…

强化学习中重要性采样

PPO 中重要性采样 https://github.com/modelscope/ms-swift/blob/main/docs/source/Instruction/GRPO/GetStarted/GRPO.md乐&#xff0c;这个网页中是的groundtruth是错误的&#xff08;可能是为了防止抄袭&#xff09;。一些例子 0. 池塘养鱼的一个例子 想象一下&#xff0c;你…

《树与二叉树详解:概念、结构及应用》

目录 一. 树的概念和结构 1.1 树的基本概念 1.2 树的结构特点 二. 树的表示方法和实际运用 2.1 孩子 - 兄弟表示法&#xff08;Child-Sibling Representation&#xff09; 2.2 树的实际应用场景 三. 二叉树的概念 3.1 二叉树的核心定义 3.2 二叉树的基本分类 四. 二叉…

Qt/C++,windows多进程demo

1. 项目概述 最近研究了一下Qt/C框架下&#xff0c;windows版本的多进程编写方法&#xff0c;实现了一个小demo。下面详细介绍一下。 MultiProcessDemo是一个基于Qt框架实现的多进程应用程序示例&#xff0c;展示了如何在Windows平台上通过共享内存和事件机制实现进程间通信。该…

Android SystemServer 系列专题【篇五:UserController用户状态控制】

本篇接着SystemServer的启动流程&#xff0c;围绕SystemServer最后阶段关于主用户的启动和解锁的流程&#xff0c;作为切入点&#xff0c;来看看SystemServer是如何讲用户状态同步到所有的系统级服务中。ssm.onStartUserssm.onUnlockingUserssm.onUnlockedUser本篇先介绍UserCo…

推荐使用 pnpm 而不是 npm

npm 的局限性 磁盘空间浪费在 npm 早期版本中&#xff0c;每个项目的node_modules目录都会完整复制所有依赖包&#xff0c;即使多个项目依赖同一个包的相同版本&#xff0c;也会重复存储。这导致磁盘空间被大量占用&#xff0c;随着项目数量的增加&#xff0c;存储成本显著上升…

Transformer实战(18)——微调Transformer语言模型进行回归分析

Transformer实战&#xff08;18&#xff09;——微调Transformer语言模型进行回归分析0. 前言1. 回归模型2. 数据处理3. 模型构建与训练4. 模型推理小结系列链接0. 前言 在自然语言处理领域中&#xff0c;预训练 Transformer 模型不仅能胜任离散类别预测&#xff0c;也可用于连…

【Linux】【实战向】Linux 进程替换避坑指南:从理解 bash 阻塞等待,到亲手实现能执行 ls/cd 的 Shell

前言&#xff1a;欢迎各位光临本博客&#xff0c;这里小编带你直接手撕&#xff0c;文章并不复杂&#xff0c;愿诸君耐其心性&#xff0c;忘却杂尘&#xff0c;道有所长&#xff01;&#xff01;&#xff01;&#xff01; IF’Maxue&#xff1a;个人主页&#x1f525; 个人专栏…

linux常用命令 (3)——系统包管理

博客主页&#xff1a;christine-rr-CSDN博客 ​​​​​ ​​ hi&#xff0c;大家好&#xff0c;我是christine-rr ! 今天来分享一下linux常用命令——系统包管理 目录linux常用命令---系统包管理&#xff08;一&#xff09;Debian 系发行版&#xff08;Ubuntu、Debian、Linux …

YOLOv8 mac-intel芯片 部署指南

&#x1f680; 在 Jupyter Notebook 和 PyCharm 中使用 Conda 虚拟环境&#xff08;YOLOv8 部署指南&#xff0c;Python 3.9&#xff09; YOLOv8 是 Ultralytics 开源的最新目标检测模型&#xff0c;轻量高效&#xff0c;支持分类、检测、分割等多种任务。 在 Mac&#xff08;…

【高等数学】第十一章 曲线积分与曲面积分——第六节 高斯公式 通量与散度

上一节&#xff1a;【高等数学】第十一章 曲线积分与曲面积分——第五节 对坐标的曲面积分 总目录&#xff1a;【高等数学】 目录 文章目录1. 高斯公式2. 沿任意闭曲面的曲面积分为零的条件3. 通量与散度1. 高斯公式 设空间区域ΩΩΩ是由分片光滑的闭曲面ΣΣΣ所围成&#x…

IDEA试用过期,无法登录,重置方法

IDEA过期&#xff0c;重置方法: IntelliJ IDEA 2024.2.0.2 (亲测有效) 最新Idea重置办法!&#xff1a; 方法一&#xff1a; 1、删除C:\Users\{用户名}\AppData\Local\JetBrains\IntelliJIdea2024.2 下所有文件(注意&#xff1a;是子目录全部删除) 2、删除C:\Users\{用户名}\App…

创建用户自定义桥接网络并连接容器

1.创建用户自定义的 alpine-net 网络[roothost1 ~]# docker network create --driver bridge alpine-net 9f6d634e6bd7327163a9d83023e435da6d61bc6cf04c9d96001d1b64eefe4a712.列出 Docker 主机上的网络[roothost1 ~]# docker network ls NETWORK ID NAME DRIVER …

Vue3 + Vite + Element Plus web转为 Electron 应用,解决无法登录、隐藏自定义导航栏

如何在vue3 Vite Element Plus搭好的架构下转为 electron应用呢&#xff1f; https://www.electronjs.org/zh/docs/latest/官方文档 https://www.electronjs.org/zh/docs/latest/ 第一步&#xff1a;安装 electron相关依赖 npm install electron electron-builder concurr…

qt QAreaLegendMarker详解

1. 概述QAreaLegendMarker 是 Qt Charts 模块中的一部分&#xff0c;用于在图例&#xff08;Legend&#xff09;中表示 QAreaSeries 的标记。它负责显示区域图的图例项&#xff0c;通常包含区域颜色样例和对应的描述文字。图例标记和对应的区域图关联&#xff0c;显示区域的名称…

linux 函数 kstrtoul

kstrtoul 函数概述 kstrtoul 是 Linux 内核中的一个函数&#xff0c;用于将字符串转换为无符号长整型&#xff08;unsigned long&#xff09;。该函数定义在 <linux/kernel.h> 头文件中&#xff0c;常用于内核模块中解析用户空间传递的字符串参数。 函数原型 int kstrtou…

LLM(三)

一、人类反馈的强化学习&#xff08;RLHF&#xff09;微调的目标是通过指令&#xff0c;包括路径方法&#xff0c;进一步训练你的模型&#xff0c;使他们更好地理解人类的提示&#xff0c;并生成更像人类的回应。RLHF&#xff1a;使用人类反馈微调型语言模型&#xff0c;使用强…