Linux系统编程系列之线程属性

news2025/1/12 22:50:24

一、什么是线程属性

        线程属性就是线程的属性,是一个用于控制线程行为和功能的参数集合。它可以影响线程的优先级、调度、同步行为和资源管理等方面。线程有许多属性,如分离属性,继承策略,调度策略等。看图

二、线程属性的特性

        1、线程优先级

        通过设置线程属性,可以控制线程在调度时的优先级。不同的操作系统和语言环境中,线程优先级的范围和具体实现可能会有所不同。

        2、线程调度

        线程属性可以控制线程的调度行为,包括线程调度算法和调度器设置。

        3、同步行为

        线程属性可以控制线程在同步时的行为。例如,可以设置线程的互斥锁或信号量的行为、线程的等待时限等。

        4、资源管理

        线程属性可以控制线程在使用资源时的行为。例如,可以设置线程对内存、网络或文件等资源的访问权限,以及资源使用超限时的响应行为。

        5、线程安全性

        线程属性可以控制线程的安全性,例如,可以设置线程的栈大小、异常处理行为,以及是否允许多个线程同时访问共享资源等。

        总之,线程属性可以为程序员提供对线程行为的细粒度控制,并帮助提高程序执行效率和稳定性。

三、线程属性的使用步骤

        由于线程属性众多,因此需要的时候不直接设置,而是先将它们置入一个统一的属性变量中,然后再以此创建线程。属性变量是一种内置数据类型,需要使用专门的函数接口进行初始化和销毁。

        1、定义且初始化属性变量 attr

        2、将所需的属性,加入 attr 中

        3、使用 attr 启动线程

        4、销毁 attr

四、重点介绍

        1、分离属性

        默认情况下,线程启动后处于可接合状态(即未分离),此时的线程可以在退出时让其他线程接合以便释放资源,但若其他线程未及时调用pthread_join()去结合它,它将成为僵尸线程,浪费系统资源。

        因此,若线程退出时无需汇报其退出值,则一般要设置为分离状态,处于分离状态下的线程在退出之后,会自动释放其占用的系统资源。有以下两种方式可以将线程设置为分离状态:

        (1)、在线程启动前,使用分离属性启动线程

        (2)、在线程启动后,使用 pthread_detach() 强制分离

        2、线程的调度策略

        静态优先级

        在Linux中,所有的任务(进程、线程在内核统称为任务,task)被分成两类静态优先级:

        (1)普通任务(静态优先级是0【动态调度策略】)

        (2)系统任务(静态优先级是1-99)

        静态优先级越大,优先级权限也越大(跟FreeRTOS的一样),普通任务是0,意味着会被任意系统任务抢占。静态优先级之所以被称为静态,是因为这种优先级一旦被设定就不能再改变,是任务本身的属性。

        调度策略有以下几种

        1、SCHED_FIFD 以先进先出的排队方式调度

        2、SCHED_RR 以轮转的方式调度

        3、SCHED_OTHER 非实时调度的普通线程(默认)

       (1)、 SCHED_FIFD

        当线程的调度策略为SCHED_FIFD时,其静态优先级必须设置为1-99,这将意味着一旦这种线程处于就绪态时,他能立即抢占任何静态优先级为0的普通线程。采用 SCHED_FIFD 调度策略的线程还遵循以下规则:

        【1】、当它处于就绪态时,就会被放入其所在优先级队列的队尾位置。

        【2】、当被更高优先级的线程抢占后,它会被放入其所在优先级队列的队头位置,当所有优先级比它高的线程不再运行后,它就恢复运行。

        【3】、当它调用 sched_yield()后,它会被放入其所在优先级队列的队尾的位置。

        总的来讲,一个具有 SCHED_FIFD 调度策略的线程会一直运行直到发送I/O请求,或者被更高优先级线程抢占,或者调用 sched_yield()主动让出CPU。

       (2)、 SCHED_RR

        当线程的调度策略为SCHED_RR时,其情况跟SCHED_FIFD是大致一样的,有一点区别:每一个SCHED_RR策略下的线程都将会被分配一个额度的时间片,当时间耗光时,它会被放入其所在优先级队列的队尾的位置。可以用 sched_rr_get_interval()来获得时间片的具体数值。

        (3)、SCHED_OTHER

        当线程的调度策略为SCHED_OTHER时,其静态优先级(static priority)必须设置为0。该调度策略是Linux系统调度的默认策略,处于0优先级别的这些线程按照所谓的动态优先级被调度,而动态优先级起始于线程的nice值,且每当一个线程已处于就绪态但被调度器调度无视时,其动态优先级会自动增加一个单位,这样能保证这些线程竞争CPU的公平性。

        动态优先级

        线程的动态优先级是非实时的普通线程独有的概念(静态优先级设置为0),它会随着线程的运行,根据线程的表现而发生改变。动态优先级数值越大,优先级越低。

        CPU消耗型线程:比如视频解码算法,这类线程只要一运行就黏住CPU不放,这样的线程的动态优先级会被慢慢地降级,这符合我们的预期,因为这类线程不需要很高的响应速度,只要保证有一定的执行时间片。

        IO消耗型线程:比如编辑器,这类线程绝大部分的时间都在睡眠,调度器发现每次调度它它都选择放弃,将CPU让给其他线程,因此会慢慢地提高它的动态优先级(nice--),这样使得这里线程在同等非实时普通线程中,有越来越高的响应速度,表现出更好的交互性能。

五、相关函数API接口

        1、初始化线程属性变量

// 初始化线程属性变量
int pthread_attr_init(pthread_attr_t *attr);

// 接口说明
    返回值:成功返回0,失败返回非零错误码
    参数attr:要初始化的线程属性变量

        2、销毁线程属性变量

// 销毁线程属性变量
int pthread_attr_destroy(pthread_attr_t *attr);

// 接口说明
    返回值:成功返回0,失败返回非零错误码
    参数attr:要销毁的线程属性变量

        3、设置线程分离属性

// 设置线程分离属性
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

// 接口说明
        返回值:成功返回0,失败返回错误码
        参数attr:线程属性变量
        参数detachstate:设置为分离属性还是接合属性,有以下两种:
            (1)、PTHREAD_CREATE_DETACHED,分离属性
            (2)、PTHREAD_CREATE_JOINABLE,接合属性

        4、设置线程静态优先级

// 设置线程静态优先级
int pthread_attr_setschedparam(pthread_attr_t *attr,
                               const struct sched_param *param);

// 接口说明
    返回值:成功返回0,失败返回错误码
    参数attr:线程属性变量
    参数param:要设置的静态优先级(0到99)

        5、 获取线程静态优先级

// 获取线程静态优先级
int pthread_attr_getschedparam(pthread_attr_t *attr,
                               struct sched_param *param);

// 接口说明
    返回值:成功返回0,失败返回错误码
    参数attr:线程属性变量
    参数param:存放获取的静态优先级

        6、设置线程动态优先级

// 设置线程动态优先级
int nice(int inc);

// 接口说明
    返回值:成功返回新的动态优先级,失败返回-1
    参数inc:动态优先级(-20到19)

六、案例

        使用线程实现数据发送和接收,设置线程属性为分离属性

// 线程的案例

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

int flag = 0;   // 简单的标志位来控制同步
char data[100];

// 线程1的例程函数,用来接收数据
void *recv_routine(void *arg)
{
    printf("I am recv_routine, my tid = %ld\n", pthread_self());

    // 设置线程分离 
    pthread_detach(pthread_self()); 

    while(1)
    {
        if(flag)
        {
            printf("pthread1 read data: %s\n", data);
            memset(data, 0, sizeof(data));
            flag = 0;
        }
    }
}

// 线程2的例程函数,用来发送数据
void *send_routine(void *arg)
{
    printf("I am send_routine, my tid = %ld\n", pthread_self());

    while(1)
    {
        printf("please input data:\n");
        fgets(data, 100, stdin);
        printf("pthread2 send data\n");
        flag = 1;
    }
}

int main(int argc, char *argv[])
{
    pthread_t tid1, tid2;

    // 创建线程1,用来接收数据
    errno = pthread_create(&tid1, NULL, recv_routine, NULL);
    if(errno == 0)
    {
        printf("pthread create recv_routine success, tid = %ld\n", tid1);
    }
    else
    {
        perror("pthread create recv_routine fail\n");
    }

    // 1、定义线程属性变量
    pthread_attr_t attr2;

    // 2、初始化线程属性变量
    pthread_attr_init(&attr2);

    // 3、设置分离属性
    pthread_attr_setdetachstate(&attr2, PTHREAD_CREATE_DETACHED);

    // 4、创建线程2,用来发送数据,线程拥有分离属性
    errno = pthread_create(&tid2, &attr2, send_routine, NULL);
    if(errno == 0)
    {
        printf("pthread create send_routine success, tid = %ld\n", tid2);
    }
    else
    {
        perror("pthread create send_routine fail\n");
    }

    // 5、销毁属性变量
    pthread_attr_destroy(&attr2);

    // 一定要加这个,否则主函数直接退出,相当于进程退出,所有线程也退出
    // 或者加上while(1)等让主函数不退出
    pthread_exit(0);
    
    return 0;
}

七、总结

        线程属性众多,一般建议把线程设置为分离属性,这样可以在线程退出后,及时释放系统资源,同时也可以根据不同的场景来设置线程的优先级。设置线程的属性需要遵循一定的步骤。

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

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

相关文章

ctfshow 命令执行(40-50)

web40 题目过滤了很多 if(isset($_GET[c])){$c $_GET[c];if(!preg_match("/[0-9]|\~|\|\|\#|\\$|\%|\^|\&|\*|\&#xff08;|\&#xff09;|\-|\|\|\{|\[|\]|\}|\:|\|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){eval($c);}}else{highlight_file(__FILE_…

pycharm 中的一个非常好使用的智能提示tabnine(大大提高代码的书写效率)

一. pycharm 中的代码智能提示插件 有时候,我们总是在写代码的时候,敲全部的代码,太过于麻烦了,如果有一个软件可以预知你的后续的操作,提前将代码给你写出来,你只需要检查对错,就可以了,这样就大大提高了工作的效率. 上面的一个插件是汉化pycharm的软件包,也是非常好用的一款…

小谈设计模式(14)—建造者模式

小谈设计模式&#xff08;14&#xff09;—建造者模式 专栏介绍专栏地址专栏介绍 建造者模式角色分类产品&#xff08;Product&#xff09;抽象建造者&#xff08;Builder&#xff09;具体建造者&#xff08;Concrete Builder&#xff09;指挥者&#xff08;Director&#xff0…

分享一款开源的QT的串口示波器

分享一款开源的QT的串口示波器&#xff0c;完全开源&#xff0c;支持串口、TCP、波形显示、通信协议。 Sailor Project功能说明 串口调试助手功能 支持传统的串口调试助手的基本收发功能&#xff0c;同时可以刷新大量的数据而不卡顿 支持保存接收的数据 支持最大200条可编辑…

cadence SPB17.4 S032 - Update Symbols失败的问题

文章目录 cadence SPB17.4 S032 - Update Symbols失败的问题概述笔记END cadence SPB17.4 S032 - Update Symbols失败的问题 概述 铺铜后, 进行DRC, 发现安装孔不太合适, 有DRC警告. 安装孔是一大孔, 上面打了一圈小孔. 这些小孔有警告, 说孔和孔之间不能干涉. 开始将这些DRC…

vue3+ts创建前端blog项目

vue3创建blog项目 cmd创建Manually select featuresChoose Vue versionUse class-style component syntax? (Y/n)Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? (Y/n)Use history mode for router?Pick a CSS pre…

基于SpringBoot的科研工作量获奖项目管理平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

TinyWebServer学习笔记-MySQL数据库连接

为什么要用数据库连接池&#xff1f; 我们的网站允许用户注册&#xff0c;在没有池的情况下&#xff0c;假设只有一个人&#xff0c;那么流程就是&#xff0c;用户点击注册&#xff0c;通过socket将用户的账号和密码发送到服务器&#xff0c;然后就需要创建MySQL数据库连接&am…

C#制做一个 winform下的表情选择窗口

能力有限&#xff0c;别人可能都是通过其他方式实现的&#xff0c;我这里简单粗暴一些&#xff0c;直接通过点击按钮后弹出个新窗体来实现。 1、先在form1上增加一个toolstrip控件&#xff0c;再增加个toolstripbutton按钮&#xff0c;用来点击后弹出新窗体&#xff0c;如图&a…

【Linux】文件权限详解

&#x1f341; 博主 "开着拖拉机回家"带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——&#x1f390;开着拖拉机回家_Linux,Java基础学习,大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341; 希望本文能够给您带来一定的…

【C++】可变参数模板使用,在emplace中应用

&#x1f30f;博客主页&#xff1a; 主页 &#x1f516;系列专栏&#xff1a; C ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ &#x1f60d;期待与大家一起进步&#xff01; 文章目录 一、可变参数模板1.参数包的展开方式1.递归方式展开2.逗号表达式展开参数包 二、empl…

王杰国庆作业day6

服务器 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <my_head.h> #define PORT 2324 //端口号 #define IP "192.168.10.107" //本机IP int main(int argc, const char *argv[]) {sqlite3* d…

FPGA project : TFT_LCD

实验目标&#xff1a; 驱动TFT_LCD显示十色彩条。 重点掌握的知识&#xff1a; 1&#xff0c;液晶显示器&#xff0c;简称LCD(Liquid Crystal Display)&#xff0c;相对于上一代CRT显示器(阴极射线管显示器)&#xff0c;LCD显示器具有功耗低、体积小、承载的信息量大及不伤眼…

pwnable_hacknote

pwnable_hacknote Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8047000)32位&#xff0c;没开PIE main部分就不贴了&#xff0c;直接贴主要的函数 unsigned int ADD() {int v0; // ebxint i; // [e…

堆栈与堆(Stack vs Heap)有什么区别?

​编写有效的代码需要了解堆栈和堆内存&#xff0c;这使其成为学习编程的重要组成部分。不仅如此&#xff0c;新程序员或职场老手都应该完全熟悉堆栈内存和堆内存之间的区别&#xff0c;以便编写有效且优化的代码。 这篇博文将对这两种内存分配技术进行全面的比较。通过本文的…

【考研复习】union有关的输出问题

文章目录 遇到的问题正确解答拓展参考文章 遇到的问题 首次遇到下面的代码时&#xff0c;感觉应该输出65,323。深入理解union的存储之后发现正确答案是&#xff1a;67,323. union {char c;int i; } u; int main(){u.c A;u.i 0x143;printf("%d,%d\n", u.c, u.i); …

Java 随机数的获得方法(5种)

1. Math.random() 静态方法 产生的随机数是 0 - 1 之间的一个 double&#xff0c;即 0 < random < 1 代码&#xff1a; 结果&#xff1a; 当调用 Math.random() 方法时&#xff0c;自动创建了一个伪随机数生成器&#xff0c;实际上用的是 new java.util.Random()。当接…

C++项目:仿mudou库one thread one loop式并发服务器实现

目录 1.实现目标 2.HTTP服务器 3.Reactor模型 3.1分类 4.功能模块划分: 4.1SERVER模块: 4.2HTTP协议模块: 5.简单的秒级定时任务实现 5.1Linux提供给我们的定时器 5.2时间轮思想&#xff1a; 6.正则库的简单使用 7.通用类型any类型的实现 8.日志宏的实现 9.缓冲区…

使用 PPG(光电容积描记图)估计心率和 SpO2 水平(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

from PIL import Image,文字成图,ImageFont import jieba分词,input优雅python绘制图片

开始的代码 import os from PIL import Image, ImageDraw, ImageFont import jiebadef generate_image_with_white_bg(text, font_path, output_path):# 设置图片大小和背景颜色image_width 800image_height 600bg_color (255, 255, 255) # 白色# 创建图片对象image Imag…