Linux的多线程(线程的创建,退出,取消请求,取消处理例程,线程属性的设置)

news2024/12/24 2:28:52

进程:是系统分配资源的最小单位,系统会为每一个进程分配一块独立的虚拟内存空间

线程:是系统调度的最小单位,系统不会为线程分配新的内存空间,但是线程也参与系统调度

cpu把时间片分给每一个进程,进程中的时间片再切分分给每一个线程,所以线程也会得到时间片,所以线程使系统调度的最小单位

线程的创建

#include <pthread.h>

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

pthread_t *thread      //线程的tid
const pthread_attr_t *attr    //线程的属性
void *(*start_routine) (void *)    //线程的任务函数
void *arg    //传递给任务函数的参数

编译时的时候记得添加  -lpthread

 void *(*start_routine) (void *) 函数指针,指向需要执行的任务函数 
 任务函数返回值必须为  void *, 参数必须为 void *,回调函数

#include <stdio.h>
#include <pthread.h>

// 任务线程
void *task1(void *arg)
{
    int j = 0;
    while (1)
    {
        printf("j=%d\n", j++);
    }
}

// 任务线程
void *task2(void *arg)
{
    int i = 0;
    while (1)
    {
        printf("i=%d\n", i++);
    }
}

// 任务线程
void *task3(void *arg)
{
    int k = 0;
    while (1)
    {
        printf("k=%d\n", k++);
    }
}

int main()
{
    pthread_t tid1;
    pthread_create(&tid1, NULL, task1, NULL);

    pthread_t tid2;
    pthread_create(&tid2, NULL, task2, NULL);

    pthread_t tid3;
    pthread_create(&tid3, NULL, task3, NULL);

    getchar();  //调用一个阻塞函数不让进程结束
}

 线程传递参数

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

struct arg
{
    int a;
    double b;
    char str[1024];
} ;

// 线程任务函数
void *task(void *arg)
{
    struct arg *p = arg;
 
    printf("整数1 %d\n", p->a);
    printf("浮点1 %f\n", p->b);
    printf("字符1 %s\n", p->str);

}

int main()
{
    struct arg arg = {15678, 156.78, "15678"};
    // 创建一个线程
    pthread_t tid;
    pthread_create(&tid, NULL, task, &arg);  //&arg是线程函数中的参数

    sleep(1);
    printf("整数2 %d\n", arg.a);
    printf("浮点2 %f\n", arg.b);
    printf("字符2 %s\n", arg.str);

    getchar(); //阻塞主进程
}

线程退出

退出当前线程

#include <pthread.h>
void pthread_exit(void *retval);

参数retval是线程的返回值,对应线程执行函数的返回值。若线程没有数据可返回则可写成NULL。

pthread_exit()用法可参照exit()

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *task(void *arg) // 子线程
{
    int i = 0;
    while (1)
    {
        printf("i=%d\n", i++);
        sleep(1);
        if (i == 10)
        {
            printf("退出子线程\n");
            pthread_exit(NULL); // 退出子线程
        }
    }
}

int main() // main 主线程
{
    // 创建线程线程
    pthread_t tid;
    pthread_create(&tid, NULL, task, NULL);

    // 默认情况,主函数结束,那么进程也会结束,所有线程都会死亡!

    printf("退出主线程\n");
    pthread_exit(NULL); // 退出  main主函数线程。只是结束线程,进程并未结束
                        // 所以就算主线程结束了子线程也还是会继续运行
}

回收一个线程资源,主线程阻塞等待子线程结束,然后回收子线程资源

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval)

thread: 需要回收资源的线程tid
retval:线程的退出参数,参数同上填NULL即可

通过阻塞等待线程执行完回收资源 

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>


// 任务线程
void *task(void *arg)
{
    int i = 0; // 局部变量,属于线程的栈空间
    while (1)
    {
        printf("线程正在执行任务 &i=%p %d\n", &i, i++);

        if (i == 10)
        {
            pthread_exit(&i); // 退出线程
        }
        sleep(1);
    }
}

int main()
{
    // 1.创建一个线程
    pthread_t tid;
    pthread_create(&tid, NULL, task, NULL);

    printf("等待线程结束回收资源\n");

    void *p = NULL;
    pthread_join(tid, &p); // 一直阻塞等待线程退出 ! 回收线程的栈空间

    printf("线程结束 退出参数:%p\n", p);
    printf("退出参数 %d\n", *((int *)p)); // 访问已经回收后的内存资源,非法访问!!

    exit(0);

}

如果没有pthread_join那么线程根本不会执行完,直接在主线程中就exit(0)退出进程了


线程取消

发送一个取消命令给线程

#include <pthread.h>
int pthread_cancel(pthread_t thread);

thread:需要取消的线程 tid
成功返回 0,失败将返回错误码

pthread_self()   返回主线程的线程号tid

线程取消请求

        当线程收到一个取消请求时,他将会如何表现取决于两个东西:一是当前的取消状态,二是当前的取消类型。

        线程的取消状态很简单--分别是PTHREADCANCEL ENABLE和 PTHREAD CANCEL DISABLE,前者是缺省的,代表线程可以接受取消请求,后者代表关闭取消请求,不对其响应。

        而在线程接受取消请求的情况下,如何停下来又取决于两种不同的响应取消请求的策略一延时响应和立即响应,当采取延时策略时,线程并不会立即退出,而是要遇到所谓的“取消点”之后,才退出。而“取消点”,指的是一系列指定的函数。

#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);//开启或关闭取消请求

state : 新的取消状态              PTHREAD_CANCEL_ENABLE 开启
                                               PTHREAD_CANCEL_DISABLE 关闭
oldstate:原来的状态

int pthread_setcanceltype(int type, int *oldtype); //设置取消类型

type:取消类型                                    PTHREAD_CANCEL_DEFERRED 延时取消 (默认类型)
                                                          PTHREAD_CANCEL_ASYNCHRONOUS 立即取消

返回值:成功 0

               失败errno
 

设置线程取消请求demo:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// 任务线程
void *task(void *arg)
{
    // 关闭取消请求
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    int i = 0;
    while (1)
    {
        printf("线程正在执行任务 %d\n", i++);
        if (i == 10)
        {
            printf("执行完毕\n");
            break;
        }
        sleep(1);
    }
    // 开启取消请求
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

    while (1)
    {
        printf("线程正在运行\n");
        sleep(1);
    }
}

int main()
{
    // 1.创建一个线程
    pthread_t tid;
    pthread_create(&tid, NULL, task, NULL);

    while (1)
    {
        printf("输入任意键取消线程\n");
        getchar();
        pthread_cancel(tid);
    }
}

 在关闭线程取消请求之后,线程无法被取消,只有在重新打开线程取消请求之后的第二个循环中才能通过pthraed_cancel取消线程

线程取消处理函数

        由于线程任何时刻都有可能持有诸如互斥锁、信号量等资源,一旦被取消很有可能导致别的线程出现死锁,因此如果一条线程的确可能被取消,那么在被取消之前必须使用以下API来为将来可能出现的取消请求注册“处理例程”,让这些例程自动释放持有的资源。

注册一个取消处理函数,当线程被取消时,会去执行该函数

#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *), void *arg);

routine: 函数指针,指向线程被取消后需要执行的函数
arg:传递给取消处理函数使用的参数

void pthread_cleanup_pop(int execute);

execute: 0 不执行,线程正常结束不执行取消处理函数 
               1 执行,线程正确结束执行取消处理函数(只要线程被取消,都会执行取消处理函数)

pthread_cleanup_push必须与 pthread_cleanup_pop 一起使用,且在同一个作用域中

注册一个线程取消处理函数demo:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
FILE *fp = NULL;

void clear_task(void *arg)
{
    printf("线程被取消,执行取消处理函数\n");
    fclose(fp); // 关闭文件,刷新缓存
}

void *task(void *arg)
{
    int i = 0;
    // 打开文件
    fp = fopen("test.txt", "w+");

    // 注册一个取消处理函数
    pthread_cleanup_push(clear_task, NULL);

    while (1) // 正在运行的时候被别人取消了
    {
        fputc('Q', fp); // 写入文件
        printf("线程正在写入数据到文件中\n");
        sleep(1);
        i++;
        if (i == 10)
        {
            break;
        }
    }

    //pthread_cleanup_pop(0); // 正常结束的时候,不执行
    pthread_cleanup_pop(1); //正常结束的时候, 执行

    printf("线程正常结束\n");
    fclose(fp); // 关闭文件,刷新缓存
}

int main()
{
    // 1.创建一个线程
    pthread_t tid;
    pthread_create(&tid, NULL, task, NULL);

    while (1)
    {
        printf("按任意键取消线程\n");
        getchar();
        pthread_cancel(tid);
    }

    return 0;
}

 在这段代码中当pthread_cleanup_pop中的值为1时,线程正常结束时也会执行clean_task中的函数,如果excute的值为0的话,那么只有在线程执行的过程中被打断会执行clean_task函数。

线程属性的设置

线程相关的api

 

 

查看系统的资源

loading@DESKTOP-R0NLPTR:~$ ulimit -a
real-time non-blocking time  (microseconds, -R) unlimited
core file size              (blocks, -c) 0              #core文件的最大值为100 blocks。
data seg size               (kbytes, -d) unlimited    #进程的数据段可以任意大。
scheduling priority                 (-e) 0        #调度优先级
file size                   (blocks, -f) unlimited     #文件可以任意大。
pending signals                     (-i) 25348       #最多有25348个待处理的信号。
max locked memory           (kbytes, -l) 64          #一个任务锁住的物理内存的最大值为32KB。
max memory size             (kbytes, -m) unlimited    #一个任务的常驻物理内存的最大值。
open files                          (-n) 1024        #一个任务最多可以同时打开1024的文件。
pipe size                (512 bytes, -p) 8        #管道的最大空间为4096字节。
POSIX message queues         (bytes, -q) 819200     #POSIX的消息队列的最大值为819200字节。
real-time priority                  (-r) 0
stack size                  (kbytes, -s) 8192        #进程的栈的最大值为10240字节。
cpu time                   (seconds, -t) unlimited    #进程使用的CPU时间。
max user processes                  (-u) 25348    #当前用户同时打开的进程(包括线程)的最大个数为25348。
virtual memory              (kbytes, -v) unlimited    #没有限制进程的最大地址空间。
file locks                          (-x) unlimited    #所能锁住的文件的最大个数没有限制。

设置线程属性的流程

1、初始化线程属性

2、设置线程属性

3、根据设置的线程属性创建线程

4、销毁线程和属性

线程属性也是线程创建的第二个参数

线程属性的创建和销毁

#include <pthread.h>

int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);

       

线程属性的设置

#include <pthread.h>

int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); //设置栈大小 
int pthread_attr_getstacksize(const pthread_attr_t *attr,

                                                   size_t *stacksize);//获取栈大小

线程栈空间的设置
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *task(void *arg)
{
    int i = 0;
    while (1)
    {
        printf("线程正在运行 %d\n", i++);
        sleep(1);

        if (i == 10)
        {
            // 退出线程
            pthread_exit(NULL);
        }
    }
}

int main()
{
    // 初始化一个线程属性
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    int n;
    printf("请输入需要设置的线程大小\n");
    scanf("%d", &n);
    // 设置线程属性

    //分配的栈空间必须比系统分配最小的栈空间要大,
    //本机最小栈空间为16384,下文给出查看最小分配栈空间办法
    int size = 16384 + n;
    // int size = 1024;
    pthread_attr_setstacksize(&attr, size);

    // 根据当前的栈大小创建线程
    pthread_t tid;
    pthread_create(&tid, &attr, task, NULL);

    // 获取当前线程的栈大小
    size_t stacksize = 0;
    pthread_attr_getstacksize(&attr, &stacksize); // 获取栈大小

    printf("当前线程的栈大小 %ld\n", stacksize);

    // 回收线程资源
    pthread_join(tid, NULL);

    // 销毁属性
    pthread_attr_destroy(&attr);
}

获取本机当前需要分配的最小栈空间

stack_min.c

#include <stdio.h>
#include <limits.h>

int main()
{
    printf("%d\n", PTHREAD_STACK_MIN);
}

可以看到本机的栈空间为16384

loading@DESKTOP-R0NLPTR:~/ gcc stack_min.c && ./a.out
16384
 线程分离属性设置

        一条线程如果是可接合的,意味着这条线程在退出时不会自动释放自身资源,而会成为僵尸线程,同时意味着该线程的退出值可以被其他线程获取。

        因此,如果不需要某条线程的退出值的话,那么最好将线程设置为分离状态,以保证该线程不会成为僵尸线程。

#include <pthread.h> 

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); 
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); 

detachstate: 线程分离属性           PTHREAD_CREATE_DETACHED 分离状态,自动回收资源 
                                                        PTHREAD_CREATE_JOINABLE   结合状态,手动回收资源 

注意:当一个线程设置为分离属性后,pthread_join 函数失效了! 因为该线程已经无需手动回收资源!没有返回值的函数是宏定义函数!

设置分离属性自动回收资源demo:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *task(void *arg)
{
    int i = 0;
    while (1)
    {
        printf("线程正在运行 %d\n", i++);
        sleep(1);

        if (i == 10)
        {
            // 退出线程
            pthread_exit(NULL);
        }
    }
}

int main()
{
    // 初始化一个线程属性
    pthread_attr_t attr;
    pthread_attr_init(&attr);

    // 设置线程分离属性
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    // 根据当前的栈大小创建线程
    pthread_t tid;
    pthread_create(&tid, &attr, task, NULL);

    printf("等待线程结束\n");
    // 回收线程资源
    pthread_join(tid, NULL); // 失效,因为线程自己回收资源!
    printf("线程资源回收\n");

    // 销毁属性
    pthread_attr_destroy(&attr);
    
    getchar();

}

主线程与子线程自动分离,子线程结束后,资源自动回收

#include <pthread.h>

int pthread_detach(pthread_t thread)

pthread_join()函数的替代函数,可回收创建时detachstate属性设置为PTHREAD_CREATE_JOINABLE的线程的存储空间。该函数不会阻塞父线程。pthread_join()函数用于只是应用程序在线程tid终止时回收其存储空间。如果tid尚未终止,pthread_detach()不会终止该线程

快速回收资源demo:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *task(void *arg)
{
    int i = 0;
    while (1)
    {
        printf("线程正在运行 %d\n", i++);
        sleep(1);

        if (i == 10)
        {
            // 退出线程
            pthread_exit(NULL);
        }
    }
}

int main()
{
    // 根据当前的栈大小创建线程
    pthread_t tid;
    pthread_create(&tid, NULL, task, NULL);

    // 快速设置分离属性
    pthread_detach(tid);
    printf("等待线程结束\n");

    // 回收线程资源
    pthread_join(tid, NULL); // 失效,因为线程自己回收资源!
    printf("线程资源回收\n");
    getchar();
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在掌控板中加载人教版信息科技教学指南中的educore库

掌控板中加载educore库 人教信息科技数字资源平台&#xff08;https://ebook.mypep.cn/free&#xff09;中的《信息科技教学指南硬件编程代码说明》文件中提到“本程序说明主要供教学参考。需要可编程主控板须支持运行MicroPython 脚本程序。希望有更多的主控板在固件中支持ed…

【重学 MySQL】五十二、MySQL8 新特性:计算列

【重学 MySQL】五十二、MySQL8 新特性&#xff1a;计算列 定义特性用法应用场景注意事项 在MySQL8中&#xff0c;计算列是一项引入的新特性&#xff0c;它为数据处理和分析提供了更大的灵活性和便捷性。 定义 计算列是指根据数据库中其他列的值通过计算得出的新列&#xff0c…

网站开发基础:HTML、CSS

前端开发主要使用的技术如 HTML、CSS 和 JavaScript 等。 简单制作一个网页 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>柒毓同学网站的首页</title><style>.c1{border: solid …

OpenGL笔记十九之相机系统

OpenGL笔记十九之相机系统 —— 2024-10-02 晚上 文章目录 OpenGL笔记十九之相机系统1.运行1.1.游戏相机1.2.轨迹球相机 2.游戏相机与轨迹球相机切换3.博主笔记本要运行需要更改的文件更改1:28_OpenGL_CameraSystem/application/Application.cpp更改2:28_OpenGL_CameraSystem/a…

YOLOv8改进,YOLOv8改进主干网络为GhostNetV3(2024年华为的轻量化架构,全网首发),助力涨点

摘要 GhostNetV3 是由华为诺亚方舟实验室的团队发布的,于2024年4月发布。 摘要:紧凑型神经网络专为边缘设备上的应用设计,具备更快的推理速度,但性能相对适中。然而,紧凑型模型的训练策略目前借鉴自传统模型,这忽略了它们在模型容量上的差异,可能阻碍紧凑型模型的性能…

查找与排序-归并排序

排序算法可以分为内部排序和外部排序&#xff0c; 内部排序是数据记录在内存中进行排序&#xff0c; 外部排序是因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;在排序过程中需要访问外存。 常见的内部排序算法有&#xff1a;插入排序、希尔排序、选择…

Arduino UNO R3自学笔记16 之 Arduino的定时器介绍及应用

注意&#xff1a;学习和写作过程中&#xff0c;部分资料搜集于互联网&#xff0c;如有侵权请联系删除。 前言&#xff1a;学习定时器的功能。 1.定时器介绍 定时器也是一种中断&#xff0c;属于软件中断。 它就像一个时钟&#xff0c;可以测量事件的时间间隔。 比如早…

操作系统学习笔记---文件管理

文件系统基础 概念 文件&#xff1a;以计算机硬盘为载体的存储在计算机上的信息集合 文件的属性 文件具有一定的属性&#xff0c;系统不同&#xff0c;属性也会有所不同&#xff0c;但通第都包括如下属性&#xff1a;名称、标识符、类型、位置、大小、保护、时间、日期和用…

vbs读取excel内容的代码-编辑代码,查找并在以下位置xls文件路径-供大家学习研究参考

用vbs读取excel的脚本&#xff0c;比较强悍。 编辑代码,查找并在以下位置xls文件路径。 strExcelPath "xls文件的路径" 当然&#xff0c;要想正确运行&#xff0c;你需得安装ms excel才行。 下载&#xff1a;https://download.csdn.net/download/weixin_43097956/…

【数据分享】2000—2023年我国省市县三级逐月植被覆盖度(FVC)数值(Shp/Excel格式)

之前我们分享过2000—2023年我国250米分辨率逐月植被覆盖度&#xff08;FVC&#xff09;栅格数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff0c;该数据来源于高吉喜等学者在国家青藏高原科学数据中心平台上分享的数据&#xff0c;合成方式采用月最大值合成&…

命令按钮QLink

主要作用用来点击后可以自动打开系统的网页浏览器&#xff0c;跳转到指定的网页 常用方法 文本 //获取和设置文本 QString text() const void setText(const QString &text)描述信息 //获取和设置描述文本 QString description() const void setDescription(const QSt…

YOLOv11改进 | 注意力篇 | YOLOv11引入ACmix注意力机制

1. ACmix介绍 1.1 摘要&#xff1a;卷积和自注意力是表示学习的两种强大技术&#xff0c;它们通常被认为是两种彼此不同的同行方法。 在本文中&#xff0c;我们表明它们之间存在很强的潜在关系&#xff0c;从某种意义上说&#xff0c;这两种范式的大量计算实际上是通过相同的操…

特殊的加法和除法(考察点为位操作符)

目录 一简介&#xff1a; 二例题讲解&#xff1a; 2.1不用加号的加法&#xff1a; 2.1.1题目&#xff1a; 2.1.2思路汇总&#xff1a; 2.1.3代码解答&#xff1a; 2.2两数相除&#xff1a; 2.2.1题目&#xff1a; 2.2.2思路汇总&#xff1a; 2.2.3代码解答&#xff1a…

第 13 章 常用类

第 13 章 常用类 文章目录 <center>第 13 章 常用类13.1 包装类13.1.1 包装类的分类13.1.2 包装类和基本数据的转换13.1.3 案例演示13.1.4 课堂测试题13.1.5 包装类型和 String 类型的相互转换13.1.6 Integer 类和 Character13.1.7 Integer 类面试题 113.1.8 Intege 类面…

【算法】0/1背包问题

背包中有一些物品&#xff0c;每件物品有它的价值与重量&#xff0c;给定一个重量&#xff0c;在该重量范围内取物品&#xff08;每件物品不可重复取&#xff09;&#xff0c;求最大价值。 将需求转化为表格&#xff0c;每一行中的每个格子代表可选哪些下标的物品在总重量限额内…

【c++】 模板初阶

泛型编程 写一个交换函数&#xff0c;在学习模板之前&#xff0c;为了匹配不同的参数类型&#xff0c;我们可以利用函数重载来实现。 void Swap(int& a, int& b) {int c a;a b;b c; } void Swap(char& a, char& b) {char c a;a b;b c; } void Swap(dou…

Linux开发讲课45--- 链表

Linux内核代码中广泛使用了数据结构和算法,其中最常用的有链表、队列kfifo、红黑树、基数树和位图。 链表 Linux内核代码大量使用了链表这种数据结构。链表是在解决数组不能动态扩展这个缺陷而产生的一种数据结构。 链表所包含的元素可以动态创建并插入和删除。链表的每个元素…

AR 领域的突破——微型化显示屏为主流 AR 眼镜铺平道路

概述 多年来&#xff0c;增强现实 (AR) 技术一直吸引着人们的想象力&#xff0c;有望将数字信息与我们的物理世界无缝融合。通过将计算机生成的图像叠加到现实世界的视图上&#xff0c;AR 有可能彻底改变我们与环境的互动方式。从增强游戏体验到协助手术室的外科医生&#xff…

【Linux】进程间关系与守护进程

超出能力之外的事&#xff0c; 如果永远不去做&#xff0c; 那你就永远无法进步。 --- 乌龟大师 《功夫熊猫》--- 进程间关系与守护进程 1 进程组2 会话3 控制终端4 作业控制5 守护进程 1 进程组 之前我们提到了进程的概念&#xff0c; 其实每一个进程除了有一个进程 ID(P…

2024/10/2 408 20题

c d d b b a b c b b a d c d a c