线程的属性 —— 分离的状态(detached state)、栈地址(stack address)、栈大小(stack size)

参考:(四十二)线程——线程属性
作者:FadeFarAway
发布时间:2017-01-17 14:09:55
网址:https://blog.csdn.net/FadeFarAway/article/details/54576771

目录

  • 引入
  • 线程属性初始化
  • 一、线程的分离状态(detached state)
    • Demo
  • 二、线程的栈地址(stack address)
  • 三、线程的栈大小(stack size)
    • Demo
  • 细节注意

引入

linux下线程的属性是可以根据实际项目需要,进行设置。之前我们讨论的线程都是采用线程的默认属性,默认属性已经可以解决绝大多数开发时遇到的问题。如我们对程序的性能提出更高的要求那么需要设置线程属性,比如可以通过设置线程栈的大小来降低内存的使用,增加最大线程个数

typedef struct
{int                 etachstate; //线程的分离状态int                 schedpolicy; //线程调度策略(线程优先级)structsched_param   schedparam; //线程的调度参数int                 inheritsched; //线程的继承性int                 scope; //线程的作用域size_t              guardsize; //线程栈末尾的警戒缓冲区大小(栈溢出时可以多溢出的大小)int                 stackaddr_set; //线程的栈设置void*               stackaddr; //线程栈的位置size_t              stacksize; //线程栈的大小
}pthread_attr_t;注:目前线程属性在内核中不是直接这么定义的,抽象太深不宜理解,为了方便,使用早期的线程属性定义,两者之间定义的主要元素差别不大。

属性值不能直接设置,必须使用相关函数进行操作初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。之后须用pthread_attr_destroy函数来释放资源

线程属性主要包括:分离的状态(detached state)、栈地址(stack address)、栈尺寸(stack size)、优先级(priority)、作用域(scope)、调度策略和参数(scheduling policy and parameters)。

默认的属性为:非绑定、非分离、缺省M的堆栈、与父进程同样级别的优先级。

线程属性初始化

  先初始化线程属性,再使用pthread_create创建线程。

#include <pthread.h>int pthread_attr_init(pthread_attr_t *attr); //初始化线程属性
int pthread_attr_destroy(pthread_attr_t *attr); //销毁线程属性所占用的资源

一、线程的分离状态(detached state)

线程的分离状态决定一个线程以什么样的方式来终止自己:

  • 非分离状态:线程的默认属性是非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。

  • 分离状态:分离线程没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。

应该根据自己的需要,选择适当的分离状态。

线程分离状态的函数:

#include <pthread.h>int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); // 设置线程属性,分离or非分离
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate); // 获取线程属性,分离or非分离pthread_attr_t *attr:被已初始化的线程属性
int *detachstate:可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)

【要注意的一点】如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timedwait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题

Demo

/**#include <pthread.h>*int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);*                              //设置线程属性,分离or非分 离*int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);*                              //获取程属性,分离or非分离*  *pthread_attr_t *attr:被已初始化的线程属性*int *detachstate:可选为PTHREAD_CREATE_DETACHED(分离线程)*                 和PTHREAD _CREATE_JOINABLE(非分离线程)*/
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>void *th_fun(void *arg)
{int n = 15;while(n--){printf("%x  %d\n",(int)pthread_self(), n);sleep(1);}return (void *)1;//由于被设置为分离态所以这个返回值不能被获取
}int main(void)
{pthread_t tid;pthread_attr_t attr;  //保存线程的属性,现在里面是垃圾值int err;pthread_attr_init(&attr);// 初始化线程属性结构体,初始化之后就保存着线程属性的默认值//参考上面,先调用初始化函数(pthread_attr_init)之后才能设置线程属性pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //设置为分离线程// 创建线程,注意第二个参数pthread_create(&tid, &attr, th_fun, NULL);//因为分离了所以会出现Invalid argument非法的参数err = pthread_join(tid, NULL);while(1){if(err != 0){printf("%s\n",strerror(err));sleep(10);pthread_exit((void *)1);}}pthread_attr_destroy(&attr);//销毁线程属性所占用的资源return 0;
}

运行结果:
在这里插入图片描述

二、线程的栈地址(stack address)

  POSIX.1定义了两个常量_POSIX_THREAD_ATTR_STACKADDR 和_POSIX_THREAD_ATTR_STACKSIZE 检测系统是否支持栈属性。也可以给sysconf函数传递_SC_THREAD_ATTR_STACKADDR或 _SC_THREAD_ATTR_STACKSIZE来进行检测。

栈有一个默认大小(点击查看博文),当进程栈地址空间不够用时,指定新建线程使用由malloc分配的空间作为自己的栈空间。通过pthread_attr_setstackaddr和pthread_attr_getstackaddr两个函数分别设置和获取线程的栈地址。传给pthread_attr_setstackaddr函数的地址是缓冲区的低地址(不一定是栈的开始地址,栈可能从高地址往低地址增长)。

#include <pthread.h>int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);
int pthread_attr_getstackaddr(pthread_attr_t *attr, void **stackaddr);attr: 指向一个线程属性的指针
stackaddr: 返回获取的栈地址
返回值:若是成功返回0,否则返回错误的编号说 明:pthread_attr_getstackaddr函数已过时,一般用下面讲到的pthread_attr_getstack来代替

三、线程的栈大小(stack size)

  当系统中有很多线程时,可能需要减小每个线程栈的默认大小,防止进程的地址空间不够用;当线程调用的函数会分配很大的局部变量或者函数调用层次很深时,可能需要增大线程栈的默认大小。
  函数pthread_attr_getstacksize和 pthread_attr_setstacksize提供获取和设置。

#include <pthread.h>int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);attr 指向一个线程属性的指针
stacksize 返回线程的堆栈大小
返回值:若是成功返回0,否则返回错误的编号

除上述对栈设置的函数外,还有以下两个函数可以获取和设置线程栈属性(上面的一组在现在的开发过程中往往不会使用

#include <pthread.h>int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize);attr 指向一个线程属性的指针
stackaddr 返回获取的栈地址
stacksize 返回获取的栈大小
返回值:若是成功返回0,否则返回错误的编号

Demo

/** 获取和设置线程的栈大小* #include <pthread.h>* int pthread_attr_setstack(pthread_attr_t *attr,*                           void *stackaddr,*                           size_t stacksize);* int pthread_attr_getstack(pthread_attr_t *attr,*                           void **stackaddr,*                           size_t *stacksize);* attr 指向一个线程属性的指针* stackaddr 返回获取的栈地址* stacksize 返回获取的栈大小* 返回值:若是成功返回0,否则返回错误的编号*/
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>#define SIZE 0x10000int print_ntimes(char *str)
{sleep(1);printf("%s\n", str);return 0;
}void *th_fun(void *arg)
{int n = 3;while (n--)print_ntimes("hello xwp\n");//在线程中也可以调用别的函数
}int main(void)
{pthread_t tid;int err, detachstate, i = 1;pthread_attr_t attr;size_t stacksize;void *stackaddr;pthread_attr_init(&attr);//初始化线程属性pthread_attr_getstack(&attr, &stackaddr, &stacksize);//获取栈信息printf("stackadd=%p\n", stackaddr);         //打印栈的地址printf("stacksize=%x\n", (int)stacksize);   //打印栈的大小/*获取当前线程是否为分离属性*/pthread_attr_getdetachstate(&attr, &detachstate);if (detachstate == PTHREAD_CREATE_DETACHED)printf("thread detached\n");else if (detachstate == PTHREAD_CREATE_JOINABLE)printf("thread join\n");elseprintf("thread un known\n");/* 设置线程分离属性*/pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);while (1){/* 在堆上申请内存,指定线程栈的起始地址和大小*/stackaddr = malloc(SIZE);//为栈分配空间、返回首地址if (stackaddr == NULL){perror("malloc");exit(1);}/* 设置栈大小和地址 大小为上面的宏,位置为上面设置的*/stacksize = SIZE;//设置占空间大小pthread_attr_setstack(&attr, stackaddr, stacksize);       //线程属性 栈地址  栈大小err = pthread_create(&tid, &attr, th_fun, NULL);//创建线程if (err != 0){printf("%s\n", strerror(err));exit(1);}printf("%d\n", i++);}pthread_attr_destroy(&attr);//销毁线程属性所占用的资源return 0;
}

运行结果:

循环创建线程,每个线程分配占用堆空间大小0x10000,最终创建2145次线程时提示空间不足,也就是堆空间被占用尽(我自己理解的)。
在这里插入图片描述

在这里插入图片描述

细节注意

1. 主线程退出其他线程不退出,主线程应调用ptrhed_exit
2. 避免僵线程(可以使用一下的方法)

1、join
2、pthread_deatch
3、pthread_create指定分离属性
注:被join线程可能在join函数返回前就释放完自己的所有内存资源,所以不应当返回被回收线程栈中的值;

3. malloc和mmap申请的内存可以被其他线程释放
4. 如果线程终止时没有释放加锁的互斥量,则该互斥量不能再被使用
5. 应避免在多线程模型中调用fork除非,马上exec,子进程中只有调用fork的线程存在,其他线程在子进程中均pthread_exit
6. 信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制

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

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

相关文章

【微信小游戏实战】零基础制作《欢乐停车场》二、关卡设计

1、游戏立项 微信小游戏中有一款《欢乐停车场Plus》的小游戏&#xff0c;大家可以搜索玩下。这是一款益智类的小游戏&#xff0c;游戏中有红、黄、绿、蓝、紫5辆豪车6个停车位&#xff0c;玩家通过可行走路线移动小车&#xff0c;最终让各颜色的小车停到对应的颜色车位&#xf…

线程同步(互斥锁、条件、读写锁、信号量)

参考&#xff1a;&#xff08;四十三&#xff09;线程——线程同步&#xff08;互斥锁、读写锁、条件变量、信号量&#xff09; 作者&#xff1a;FadeFarAway 发布时间&#xff1a;2017-01-17 21:25:28 网址&#xff1a;https://blog.csdn.net/FadeFarAway/article/details/545…

Nginx使用Expires增加浏览器缓存加速(转)

转载自&#xff1a;Nginx使用Expires增加浏览器缓存加速 Nginx可以更改HTTP头部&#xff0c;这个是Web服务器必须的&#xff0c;当然Nginx更可以支持在HTTP头部中添加Expires等相关信息&#xff0c;增强浏览器缓存&#xff0c;是网页加载速度增强。 Nginx中使用Expires增强浏览…

Linux curl命令详解

转自&#xff1a;https://www.cnblogs.com/duhuo/p/5695256.html 命令&#xff1a;curl 在Linux中curl是一个利用URL规则在命令行下工作的文件传输工具&#xff0c;可以说是一款很强大的http命令行工具。它支持文件的上传和下载&#xff0c;是综合传输工具&#xff0c;但按传统…

MAC地址、IP地址、ARP协议

B站视频&#xff1a;计算机网络微课堂&#xff08;有字幕无背景音乐版&#xff09; 网址&#xff1a;https://www.bilibili.com/video/BV1c4411d7jb?p61 说明&#xff1a;讲的不错&#xff0c;后期可以继续看此视频学习网络知识 目录MAC地址IP地址ARP协议MAC地址 IP地址 ARP协…

编程学习网站

2019独角兽企业重金招聘Python工程师标准>>> ‍ 刚开始学习编程&#xff0c;如果只是拿着书本拼命敲代码&#xff0c;肯定是非常枯燥的。如果不是非常有决心和毅力可能就会轻易放弃了&#xff0c;有没有学习坡度不那么陡的教程呢&#xff1f; 让我们可以先学点基本的…

ARM指针寄存器——堆栈指针寄存器SP、程序计数器PC、连接寄存器LR

参考&#xff1a;堆栈指针寄存器 SP详解以及栈的作用 作者&#xff1a;蓝色鲜橙多 网址&#xff1a;https://blog.csdn.net/qq_36588941/article/details/89873633?utm_sourceapp&app_version4.16.0&codeapp_1562916241&uLinkIdusr1mkqgl919blen 目录堆栈的实现方…

U-Boot 图形化配置及其原理

目录U-Boot 图形化配置体验&#xff08;如何使能dns 命令&#xff09;menuconfig 图形化配置原理make menuconfig 过程分析Kconfig 语法简介1、mainmenu2、调用其他目录下的Kconfig 文件3、menu/endmenu 条目3、config 条目4、depends on 和select4、choice/endchoice5、menuco…

类与对象(三)

定义一个描述教师的类Teacher&#xff0c;数据成员包括工号(Num),姓名(Name、性别(Sex、家庭住址( Addr}、联系电话(Tel}, E-mail地址(Email )、职务(Headship )、职称(Post)和工资(Salary对于数据成员&#xff0c;要求用字符数组实现工号、姓名、家庭住址、联系电话、E-mail地…

OpenStack tokens id获取测试

转载于:https://www.cnblogs.com/heidsoft/p/3524711.html

Linux 内核获取、初次编译、源码目录分析

目录Linux 内核获取Linux 内核初次编译Linux 内核源码目录分析1、arch 目录2、block 目录3、crypto 目录4、Documentation 目录5、drivers 目录6、firmware 目录7、fs 目录8、include 目录9、init 目录10、ipc 目录11、kernel 目录12、lib 目录13、mm 目录Linux 内核获取 关于L…

PHP里10个鲜为人知但却非常有用的函数

PHP里有非常丰富的内置函数&#xff0c;很多我们都用过&#xff0c;但仍有很多的函数我们大部分人都不熟悉&#xff0c;可它们却十分的有用。这篇文章里&#xff0c;我列举了一些鲜为人知但会让你眼睛一亮的PHP函数。 levenshtein() 你有没有经历过需要知道两个单词有多大的不同…

学会阅读硬件的原理图、数据手册大全

参考&#xff1a; 郭天祥&#xff1a;https://www.bilibili.com/video/BV1DW411a7mz?p8 韦东山&#xff1a;https://www.bilibili.com/video/BV1ga4y1Y7PL?p4 https://www.bilibili.com/video/BV17g411F7oR?spm_id_from333.999.0.0 洋桃电子&#xff1a;https://www.bilibil…

解决替换weblogic的commons-fileupload.jar后引发的问题

为什么80%的码农都做不了架构师&#xff1f;>>> 上一篇博文中提到通过替换weblogic自带的commons-fileupload.jar来解决FileUpload类的NoSuchMethodError问题。在完成替换后&#xff0c;该问题得到顺利解决&#xff0c;但是也会引发其他的一些问题&#xff0c;下面…

Python for Data Analysis 学习心得(二) - pandas介绍

一、pandas介绍 本篇程序上篇内容&#xff0c;在numpy下面继续介绍pandas&#xff0c;本书的作者是pandas的作者之一。pandas是非常好用的数据预处理工具&#xff0c;pandas下面有两个数据结构&#xff0c;分别为Series和DataFrame&#xff0c;DataFrame之前我在一些实战案例中…

Euler:欧拉函数&素数筛

一、欧拉函数 欧拉函数是小于x的整数中与x互质的数的个数&#xff0c;一般用φ(x)表示。 通式&#xff1a; 其中p1, p2……pn为x的所有质因数&#xff0c;x是不为0的整数。比如x12&#xff0c;拆成质因数为122*2*3&#xff0c;12以内有1/2的数是2的倍数&#xff0c;那么有1-1/2…

大小端字节序

想起以前在汇编语言和数字逻辑的时候也有接触到一些这个概念&#xff0c;已经有点模糊了&#xff0c;搞不清楚哪个是低位在前哪个是高位在前。后来在Wiki和Google的帮助下也算摸清楚了一些Endianness的概念。 一、字节序的起源 在计算机中&#xff0c;字节序&#xff08;Endian…

docker 部署nginx 使用keepalived 部署高可用

一&#xff0e;体系架构 在Keepalived Nginx高可用负载均衡架构中&#xff0c;keepalived负责实现High-availability (HA) 功能控制前端机VIP&#xff08;虚拟网络地址&#xff09;&#xff0c;当有设备发生故障时&#xff0c;热备服务器可以瞬间将VIP自动切换过来&#xff0c…

虚拟字符设备驱动开发步骤

目录前言字符设备驱动简介内核驱动操作函数集合(file_operations结构体)字符设备驱动开发步骤.ko驱动模块的加载和卸载(module_init驱动入口、insmod驱动加载)字符设备注册与注销到内核register_chrdev(设备号、设备名) -- 很少用了实现设备的具体操作函数添加LICENSE 和作者信…

设计模式20——Mediator设计模式

2019独角兽企业重金招聘Python工程师标准>>> Mediator中介者设计模式是通过一个中介对象封装一系列关于对象交互行为. Mediator中介者设计模式中的角色如下&#xff1a; (1).中介者&#xff08;Mediator&#xff09;&#xff1a;抽象定义了“同事”&#xff08;co…