当前位置: 首页 > 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.dtcms.com/a/336447.html

相关文章:

  • 工作八年记
  • 官方正版在线安装office 365安装工具
  • 数组的三种主要声明方式
  • 大模型对齐算法(二): TDPO(Token-level Direct Preference Optimization)
  • Android中使用Compose实现各种样式Dialog
  • tcp会无限次重传吗
  • Eclipse Tomcat Configuration
  • Portkey-AI gateway 的一次“假压缩头”翻车的完整排障记:由 httpx 解压异常引发的根因分析
  • 学习日志36 python
  • 力扣经典算法篇-52-零钱兑换(动态规划)
  • Java语法进阶之常用类
  • 【C2000】德州仪器C2000产品整体介绍
  • http工作流程
  • LangChain 多任务应用开发
  • matlab tlc的文件、字符串操作
  • Python @staticmethod 装饰器与 staticmethod() 函数
  • Tomcat Session Replication Cluster:实现高可用性和可扩展性的关键
  • 机试备考笔记 14/31
  • Ugit使用记录
  • Next.js跟React关系(Next.js是基于React库的全栈框架)(文件系统路由、服务端渲染SSR、静态生成SSG、增量静态再生ISR、API路由)
  • 提升 LLM 推理效率的秘密武器:LM Cache 架构与实践
  • Pandas初学者入门
  • C语言中回调函数的作用
  • 2025.8.11-2025.8.17第33周:完成第一次头马备稿演讲
  • 北京JAVA基础面试30天打卡12
  • 【URP】[法线贴图]为什么主要是蓝色的?
  • ZipList优缺点总结
  • leetcode_438 找到字符串中的所有异位词
  • 代码随想录刷题Day34
  • 上位机知识篇---静态库