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

Linux应用软件编程---多任务(线程)(线程创建、消亡、回收、属性、与进程的区别、线程间通信、函数指针)

线程

一、什么是线程?

    线程是操作系统任务调度的最小单位。是一个轻量级的进程,可实现多任务并发。

二、线程的创建

    线程由某个进程创建。
进程创建线程时,会为其分配独立的(8M)栈区空间;线程和所在进程,以及进程中的其他线程,共用进程的堆区、数据区、文本区。

三、线程的调度

    宏观并行,微观串行。

四、线程的消亡

1.  线程退出

    1)在线程任务函数中使用 return 结束线程;

    2)使用 pthread_exit() 函数结束线程。

2. 回收线程资源空间

    使用 pthread_join() 函数回收资源空间。

五、进程和线程的区别

1、进程

    1)进程是操作系统资源分配的最小单位。
2)资源消耗:进程资源开销大,每次创建都需要有0-4G的虚拟内存空间。
3)效率角度:由操作系统创建,创建时耗时比线程大;跨进程调度比跨线程调度慢。
4)通信方面: 进程间不能直接通信,需要使用进程间通信机制(IPC机制)。
5)安全性角度:进程安全性比线程高,因为各进程空间独立。

2、线程

    1)线程是操作系统任务调度的最小单位。
2)资源消耗:资源开销较小,只需要所在进程为其开辟8M的栈区空间。
3)效率角度:由所在进程创建;跨进程调度比跨线程调度慢。
4)通信方面:通信简单,可以使用线程共享的区域进行通信(比如全局变量)。
5)安全性角度:线程没有进程安全性好,一个线程异常可能影响同一进程中的所有线程。

六、线程的相关编程

    均需包含头文件:#include<pthread.h>

   * 注:1. 线程编译时需确保进程未被关闭,即 要主函数末尾使用 sleep(1) 或 while(1){} 来解决;

            2. 编译时,需加上“ -pthread ”,例如: gcc pthread_main.c -pthread 。

1、线程的创建

         pthread_create()  创建线程

         pthread_self()  获取当前线程的ID号

int pthread_create(pthread_t *thread,const pthread_attr_t *attr,

                              void *(*start_routine) (void *),void *arg);

功能:创建一个新的线程
参数:
thread : 保存线程ID的变量地址
attr:线程属性的对象地址
为 NULL 时 : 按照默认属性创建
start_routine (函数的指针):指向线程启动后要执行的任务(线程任务函数)
arg:为线程任务函数传递的参数
返回值:
成功:0

       失败:非0

 /**** void *(*start_routine) (void *)****/
函数指针:一个指向函数的指针
类型:指针(指向的对象是函数:void *() (void *);) 
指针的名称:start_routine

2、线程的调度

        由操作系统调度。

3、线程的消亡

    1)线程退出

    (1)在线程任务函数中使用 return 结束线程

    (2)使用 pthread_exit() 函数结束线程

void pthread_exit(void *retval)

功能:用来在某时结束一个线程

参数:

        retval:向回收的线程传递的参数的地址

                为 NULL :表示不传递参数  eg. pthread_exit(NULL)

返回值:

        成功:0

        失败:非0

    2)线程回收

        使用 pthread_join() 函数回收线程资源空间

int pthread_join(pthread_t thread, void **retval)

功能:阻塞等待回收线程资源空间

参数:

        thread:要回收的线程ID号

        retval:用来保存线程退出时传递的参数

                为 NULL :不接收传递的参数

返回值:

        成功:0

        失败:-1

七、线程的属性

1、分离属性

    不需要被其他线程回收的线程称为分离属性的线程,将来被操作系统回收。

2、非分离属性

    可以被其他线程回收或结束的线程称为非分离属性线程。

默认线程的属性为:非分离属性

八、线程的回收策略

1、分离属性的线程

    不需要回收,自动被操作系统回收。

    可采用 pthread_detach() 函数将线程设置成具有分离属性的线程。

int pthread_detach(pthread_t thread);

功能:将线程设置成具有分离属性的线程

参数:
thread:该线程的ID号

2、非分离属性的线程

    采用 pthread_join() 函数来阻塞等待回收。

九、线程间通信

    在全局变量、全局队列、共享内存区域通信。

1、原因

    因为多个线程在访问临界资源时存在资源竞争。

    临界资源:多个线程可以同时访问的资源,比如全局变量、共享内存空间等。

2、如何解决资源竞争问题

    互斥机制:多个线程访问临界资源时,具有排他性的机制 (一次只允许访问一个线程对该临界资源进行访问)。

    即,使用 互斥锁 解决资源竞争问题。

3、实现步骤

    1)创建互斥锁:pthread_mutex_t

    2)初始化互斥锁:pthread_mutex_init()

int pthread_mutex_init(pthread_mutex_t *restrict mutex,

                                     const pthread_mutexattr_t *restrict attr);

功能:初始化互斥锁

参数:

        mutex:锁对象的地址

        attr:锁的属性

                为 NULL :默认属性

返回值:

        成功:0

        失败:-1

eg. pthread_mutex_t mutex;

      pthread_mutex_init(&mutex, NULL);

    3)加锁:pthread_mutex_lock()

int pthread_mutex_lock(pthread_mutex_t *mutex);

    4)解锁:pthread_mutex_unlock()

int pthread_mutex_unlock(pthread_mutex_t *mutex);

    5)销毁锁:pthread_mutex_destroy()

int pthread_mutex_destroy(pthread_mutex_t *mutex);

十、应用示例

1、创建一个线程,并在其中打印一个学生信息

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>typedef struct student
{int id;char name[32];
}str;void *task(void *arg)
{struct student *s = ((struct student *)arg);  // ->//struct student s = *((struct student *)arg);  // .for(int i = 0; i < 2; i++){printf("tid = %lx\n", pthread_self());printf("%d %s %d", s->id, s->name);}return NULL;
}int main(void)
{pthread_t tid;struct student str[1] = {1, "zhangsan"};int ret = pthread_create(&tid, NULL, task, str);if(ret != 0){printf("pthread_create error!\n");return -1;}sleep(2);/*或while(1){}*/return 0;}

2、使用线程同时执行五个任务模块。

#include <unistd.h>
#include <fcntl.h>
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>typedef void *(*PFUN_t)(void *); //函数指针void *main_ctl(void *arg)
{while (1){printf("主控模块正在工作..pid = %d\n", getpid());sleep(1);}
}void *get_ctr(void *arg)
{ while (1){printf("获取指令模块正在工作...pid = %d\n", getpid());sleep(1);}
}void *put_ctr(void *arg)
{while (1){printf("指令控制模块正在工作...pid = %d\n", getpid());sleep(1);}
}void *get_pic(void *arg)
{ while (1){printf("图像采集模块正在工作...pid = %d\n", getpid());sleep(1);}
}void *put_pic(void *arg)
{while (1){printf("图像发送模块正在工作...pid = %d\n", getpid());sleep(1);}
}int main(int argc, const char *argv[])
{//主进程、主线程pthread_t tid[5] = {0};PFUN_t tasks[5] = {main_ctl, get_ctr, put_ctr, get_pic, put_pic}; //函数指针数组for(int i = 0; i < 5; i++){int ret = pthread_create(&tid[i], NULL, tasks[i], NULL);if (ret !=0){printf("pthread_create error\n");return -1;}}while (1)/***确保进程没有关闭***/{}return 0;
}

3、使用互斥锁完成两个线程任务的有序打印。

#include <stdio.h>
#include <pthread.h>int num_g = 0;
pthread_mutex_t mutex; //全局区 互斥锁创建void *task1(void *arg)
{for(int i = 0; i < 1000; i++){pthread_mutex_lock(&mutex); //加锁num_g = num_g + 1;printf("num_g = %d\n", num_g);pthread_mutex_unlock(&mutex); //解锁}
}void *task2(void *arg)
{for(int i = 0; i < 1000; i++){pthread_mutex_lock(&mutex); //加锁num_g = num_g + 1;printf("num_g = %d\n", num_g);pthread_mutex_unlock(&mutex); //解锁}
}int main(void)
{pthread_t tid[2];//保存线程ID的变量地址pthread_mutex_init(&mutex, NULL);//初始化互斥锁/*****创建线程*****/pthread_create(&tid[0], NULL, task1, NULL);pthread_create(&tid[1], NULL, task2, NULL);/*****线程回收*****/pthread_join(tid[0], NULL);pthread_join(tid[1], NULL);/*****互斥锁的销毁*****/pthread_mutex_destroy(&mutex);return 0;
}

十一、函数指针

1、定义

    返回值类型 ( *指针名称)(形参表)

eg. void *(*fun)(void *)

2、函数指针初始化

    返回值类型 ( *指针名称)(形参表) = 函数的地址

eg. void *(*fun)(void *) = main_ctl
  main_ctl 为指向的函数

3、函数指针赋值

    返回值类型 ( *指针名称)(形参表) = 函数的入口地址

 eg.  void *(*fun)(void *) = NULL;
        fun = main_ctl

4、函数指针的使用

    函数指针(实参表)

5、函数指针的数组

    返回值类型 ( *数组名称[n])(形参表)

eg. void *( *fun[5])(void *);

【END】

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

相关文章:

  • (一)React企业级后台(Axios/localstorage封装/动态侧边栏)
  • Android 对话框 - 基础对话框补充(不同的上下文创建 AlertDialog、AlertDialog 的三个按钮)
  • WPFC#超市管理系统(6)订单详情、顾客注册、商品销售排行查询和库存提示、LiveChat报表
  • C#WPF实战出真汁13--【营业查询】
  • [辩论] TDD(测试驱动开发)
  • ZKmall开源商城的移动商城搭建:Uni-app+Vue3 实现多端购物体验
  • Collections.synchronizedList是如何将List变为线程安全的
  • Trae 辅助下的 uni-app 跨端小程序工程化开发实践分享
  • 李宏毅NLP-11-语音合成
  • 在 Element UI 的 el-table 中实现某行标红并显示删除线
  • 【PHP】Hyperf:接入 Nacos
  • Centos中内存CPU硬盘的查询
  • vscode无法检测到typescript环境解决办法
  • OpenCV 图像处理核心技术:边界填充、算术运算与滤波处理实战
  • 大模型应用发展与Agent前沿技术趋势(中)
  • JVM常用工具:jstat、jmap、jstack
  • 【Linux】IO多路复用
  • 17-线程
  • Python自学10-常用数据结构之字符串
  • Python异常、模块与包(五分钟小白从入门)
  • 文件快速复制工具,传输速度提升10倍
  • riscv中断处理软硬件流程总结
  • 【C语言强化训练16天】--从基础到进阶的蜕变之旅:Day6
  • Vue3 中的 ref、模板引用和 defineExpose 详解
  • 安卓14系统应用收不到开机广播
  • 【Java后端】Spring Boot 集成 MyBatis-Plus 全攻略
  • 大模型算法岗面试准备经验分享
  • (机器学习)监督学习 vs 非监督学习
  • 智能制造——解读37页 案例分享灯塔工厂解决方案【附全文阅读】
  • 电子电气架构 --- 自动驾驶汽车的下一步发展是什么?