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

嵌入式学习 D29:系统编程--线程

  • 线程的基本概念
  • 线程实现原理
  • 线程相关函数
  • 线程与进程的区别

一、线程的基本概念

        Linux中,线程又叫做轻量级进程(light-weight process LWP),也有PCB,创建线程使用的底层函数和进程底层一样,都是clone,但没有独立的地址空间;而进程有独立地址空间,拥有PCB。
        Linux下:线程是最小的执行单位,调度的基本单位。进程是最小分配资源单位,可看成是只有一个线程的进程。

        特征:

        1)共享资源;

        2)效率高;

        3)三方库:pthread  clone  posix  

        编写代码头文件:pthread.h

        编译代码加载库:-lpthread     gcc 1.c -lpthread

二、多线程程序设计步骤

1.创建多线程(只要创建成功就启动了

2.线程空间操作(设计函数表达要实现什么功能

3.线程资源回收(栈区回收,默认进程结束栈区不释放

注:

(1)进程中的第一个线程为主线程,主线程有且仅有一个,(主线程号和进程号一

致)其它次线程为平级关系;

(2)主线程不需创建,运行a.out就出来了,后调用函数创建其他空间,在进程空间新

开栈区(8M)。

三、线程相关函数

1. pthread_create函数:创建指定的一个线程(功能)

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

参数:

  thread 线程id,需要实现定义并由该函数返回。

  attr   线程属性,一般是NULL,表示默认属性。

  start_routine 指向指针函数的函数指针, 本质上是一个函数的名称即可。称为th 回调函数,是线程的执行空间。

返回值:成功返回0;失败返回相应的错误码。

注意:

  • 一次pthread_create执行只能创建一个线程。

  • 每个进程至少有一个线程称为主线程。

  • 主线程退出则所有创建的子线程都退出。

  • 主线程必须有子线程同时运行才算多线程程序。

  • 线程id是线程的唯一标识,是CPU维护的一组数字。

  • pstree 查看系统中多线程的对应关系。

  • 多个子线程可以执行同一回调函数。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>void *th1 (void *arg)
{while(1){printf("发送视频\n");sleep(1);}
}
void *th2 (void *arg)
{while(1){printf("接收控制\n");sleep(1);}
}
int	main(int argc, char **argv)
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);while(1)sleep(1);//system("pause");return 0;
}

2.pthread_exit函数:获取当前线程的线程id(谁调就返回谁的id号)

pthread_t pthread_self(void);

返回值:成功即返回当前线程的线程id;  失败  -1。

用户层表示(ps命令)与内核层表示(数字大)不同,使用内核层表示用户层查看(man ps     ps-eLf)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
void *th1 (void *arg)
{while(1){printf("发送视频 th1, tid:%ld\n",pthread_self());sleep(1);}
}
void *th2 (void *arg)
{while(1){printf("接收控制th2, tid:%ld\n",pthread_self());sleep(1);}
}int	main(int argc, char **argv)
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);while(1){printf("main th, tid:%ld\n",pthread_self());sleep(1);}return 0;
}

3. pthread_exit函数:自行退出 = =自杀 ==子线程自己退出

void pthread_exit(void *retval); exit return p;

参数: retval 线程退出时候的返回状态,临死遗言。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>void *th(void *arg)
{int i = 3;while (i--){printf("th, tid:%ld\n",pthread_self());sleep(1);}pthread_exit(NULL);
}int	main(int argc, char **argv)
{pthread_t tid;pthread_create(&tid,NULL,th,NULL);while(1)sleep(1);system("pause");return 0;
}

4.pthread_cancel函数:强制退出 ==他杀 ==主线程结束子线程

int pthread_cancel(pthread_t thread);

参数:thread 请求结束一个线程id

返回值:成功返0,失败返-1。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>void *th(void *arg)
{int i = 3;while (i){printf("th, tid:%ld\n",pthread_self());sleep(1);}pthread_exit(NULL);
}int	main(int argc, char **argv)
{pthread_t tid;pthread_create(&tid,NULL,th,NULL);int i = 0;while(1){printf("main th, tid:%ld\n",pthread_self());sleep(1);i ++;if( 3 == i){pthread_cancel(tid);}}system("pause");return 0;
}

线程的回收机制 

1、不同与进程没有孤儿线程和僵尸线程;

2、主线程结束任意生成的子线程都会结束;

3、子线程的结束不会影响主线程的运行。

5.pthread_join函数:线程回收

通过该函数可以将指定的线程资源回收(线程结束栈区默认没被释放),该函数具有 阻塞等待功能,如果指定的线程没有结束,则回收线程会阻塞。

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

参数:thread为要回收的子线程id;retval为要回收的子线程返回值状态;

:二级指针需要改变指针的指向

返回值:成功返0;失败返-1。

扩展:Tcb块(线程控制块),进程中有10个线程,pcb块中就有10个tcb块。

             次线程返回的不能为局部变量(解决办法:加static或malloc申请(+free()))

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>void *th(void *arg)
{int i = 3;while (i--){printf("th, tid:%ld\n",pthread_self());sleep(1);}return NULL;
}int	main(int argc, char **argv)
{pthread_t tid;pthread_create(&tid,NULL,th,NULL);pthread_join(tid,NULL);system("pause");return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>void *th(void *arg)
{// static char str[] = "我要消亡了";char *p = malloc(20);strcpy(p,"我要消亡了");sleep(3);// return str;return p;
}int	main(int argc, char **argv)
{pthread_t tid;pthread_create(&tid,NULL,th,NULL);void *ret;pthread_join(tid,&ret);printf("ret %s\n",(char*)ret);//void* ->qitaleixingzhizhen qiangzhuangfree(ret);system("pause");return 0;
}

 次线程的回收策略:

1、如果预估子线程可以有限范围内结束则正常用pthread_join等待回收;

2、如果预估子线程可能休眠或者阻塞则等待一定时间后强制回收;

3、如果子线程已知必须长时间运行则,不再回收其资源。

6.线程的传参:降低程序耦合性(传参>定义全局变量)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>typedef struct 
{char name[50];int age;char addr[50];
}PER;void *th(void *arg)
{PER *per = (PER*)arg;printf("th:%s %d %s\n",per->name,per->age,per->addr);strcat(per->name,"1");return per;
}int	main(int argc, char **argv)
{PER per;bzero(&per,sizeof(per));printf("pls name:");fgets(per.name,sizeof(per.name),stdin);//zhang\nper.name[strlen(per.name) - 1]= '\0';char buf[5] = {0};printf("pls age:");fgets(buf,sizeof(buf),stdin);per.age = atoi(buf);printf("pls addr:");fgets(per.addr,sizeof(per.addr),stdin);per.addr[strlen(per.addr) - 1]= '\0';pthread_t tid;pthread_create(&tid,NULL,th,&per);//ren yi lei xing dao void* bu yong qiang zhuanvoid *ret = NULL;pthread_join(tid,&ret);printf("join ret:%s %d %s\n",((PER*)ret)->name,((PER*)ret)->age,((PER*)ret)->addr);// pthread_join(tid,NULL);// printf("join ret:%s %d %s\n",per.name,per.age,per.addr);system("pause");return 0;
}

7.线程分离属性:线程消亡,自动回收空间

 pthread_attr_init()函数:初始化一个attr的变量

int pthread_attr_init(pthread_attr_t *attr);

参数:attr,需要变量来接受初始值。

返回值:成功返0,失败返非0 (错误)

 pthread_attr_destroy()函数:销毁attr变量

int pthread_attr_destroy(pthread_attr_t *attr);

参数:attr,属性变量

返回值:成功返0,失败返非0(错误)

pthread_attr_setdetachstate()函数:把一个线程设置成相应的属性

 int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

参数:attr,属性变量,有init函数初始化他。
                    detachstate:有2个可选值,
                    PTHREAD_CREATE_DETACHED

pthread_attr_deatch()函数:设置分离属性:线程id号,填自己的id

8.线程清理函数

pthread_cleanup_push:注册一个线程清理函数

void pthread_cleanup_push(void (*routine)(void *), void *arg);

参数:routine,线程清理函数的入口
                    arg,清理函数的参数。

pthread_cleanup_pop:调用清理函数

void pthread_cleanup_pop(int execute);

参数:execute,非0 执行清理函数
                           0 ,不执行清理

注:pthread_cleanup_push与pthread_cleanup_pop函数需成对出现;

四、线程与进程的区别

进程可以启动线程,线程也可启动进程(进程中的多线程变为单线程)

1、资源:

(1)线程比进程多了共享资源;

(2)线程又具有部分私有资源;

(3)进程间只有私有资源没有共享资源。

2、空间:

(1)进程空间独立(写时复制),不能直接通信;

(2)线程可以共享空间(栈区默认不共享),可以直接通信。

3、不同点:

(1)创建开销不一样,进程创建需要3G空间,线程创建只需要8M,线程并发度高于进

        程;

(2)进程变量不共享;

(3)进程切换(复杂)需要的缓存空间多,线程切换栈区,PC指针也发生变化;

(4)进程可申请到硬件资源;

4、稳定性差异:

(1)线程稳定性差(其中一个线程崩溃或严重异常,进程直接结束);

(2)项目任务复杂,用进程做;简单小任务,用线程做;

5、共同点:都能并发。

6、考虑用线程还是进程的两个准则:

(1)稳定性;

(2)看资源够不够用(够用用线程)。

线程的优缺点

优点:比多进程节省资源,可以共享变量。

缺点:1.线程和进程相比,稳定性,稍微差些

        2.线程的调试gdb,相对麻烦些,因为并发

http://www.xdnf.cn/news/673507.html

相关文章:

  • AbMole| MGCD0103(M1790,Mocetinostat)
  • 深入解析Google多线程环境下的空间配置器——TCMalloc
  • 哈希算法及其在文件唯一性判定中的应用
  • [Vue]浅浅了解vue3响应式的基本原理
  • 【c++】exe找不到dll里的符号:error LNK2019: unresolved external symbol
  • (LeetCode 每日一题)2894. 分类求和并作差(数组、数学)
  • 优秘AI短视频数字人6月功能更新预告:新增多个AIGC热门功能,智能体和知识库再升级
  • 11.13 LangGraph记忆机制解析:构建生产级AI Agent的关键技术
  • MyBatis-Plus一站式增强组件MyBatis-Plus-kit(更新2.0版本):零Controller也能生成API?
  • 数据链路层
  • 动态规划(8):路径问题
  • cos和dmz学习
  • docker-compose搭建emqx 服务
  • 大学大模型教学:基于NC数据的全球气象可视化解决方案
  • 计算机组成原理:IEEE 754标准
  • 武汉火影数字VR大空间制作
  • Spring Cloud 详解:2025 最新技术与最佳实践
  • 第二章 1.4 数据采集安全风险防范之数据分类分级
  • vue + ant-design + xlsx 实现Excel自定义模板导入功能
  • 打卡day38
  • 基于vue框架的动物园饲养管理系统a7s60(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • SSM-IOC入门案例/DI入门案例
  • OpenHarmony平台驱动使用(四),GPIO
  • 08SpringBoot高级--自动化配置
  • 3D虚拟工厂
  • leetcode每日一题(好几天之前的) -- 3068.最大节点价值之和
  • 国产化Word处理控件Spire.Doc教程:在 C# 中打印 Word 文档终极指南
  • 李沐《动手学深度学习》 | 4.5-4.6 正则化技术:权重衰退与Dropout
  • 类和对象(3)
  • openpi π₀ 项目部署运行逻辑(五)——模型微调