多线程编程——线程的属性

以下内容源于C语言中文网的学习与整理,非原创,如有侵权请告知删除。

如果只是学习简单的多线程编程,本文内容可以暂时忽略。在利用pthread_create() 函数创建线程时,第二个参数设置为NULL即可。

一、引入

通过阅读前面章节,我们已经学会了如果创建一个线程,例如:

#include <pthread.h>
void * threadFun(void* args){//......
}
pthread_t myThread;
pthread_create(&myThread, NULL, ThreadFun, NULL);

其中pthread_create() 函数需要传递 4 个参数,第二个参数 NULL 表示以系统默认的属性创建线程。那么线程都有哪些属性,线程的属性又该如何设置或者修改呢?

二、线程属性的种类

POSIX 标准中,线程的属性用 pthread_attr_t 类型的变量表示,举个例子:

#include <pthread.h>pthread_attr_t myAttr;

上面我们就定义了一个表示线程属性的变量。

使用此变量前,必须调用 pthread_attr_init() 函数进行初始化,该函数的语法格式如下:

int pthread_attr_init(pthread_attr_t * attr);

此函数定义在 <pthread.h> 头文件中,函数执行成功时,返回数字 0,反之返回非零数。

例如,对 myAttr 变量进行初始化:

pthread_attr_init(&myAttr);

通过调用 pthread_attr_init() 函数,myAttr 变量就拥有了系统默认的线程属性。在此基础上,我们可以根据需要对 myAttr 变量的属性值进行修改。

在利用pthread_create() 函数创建线程时,将这个修改后的变量放在第二个参数位置即可。

pthread_attr_t 是一种结构体类型,内部包含多种线程属性:

typedef struct
{int __detachstate;int __schedpolicy;struct sched_param __schedparam;int __inheritsched;int __scope;size_t __guardsize;int __stackaddr_set;void* __stackaddr;size_t __stacksize;
} pthread_attr_t;

接下来,我们将从中挑选出几个常用的属性,给您讲解它们的功能以及修改的方法。

1) __detachstate

我们知道,默认属性的线程在执行完目标函数后,占用的私有资源并不会立即释放,要么执行完 pthread_join() 函数后释放,要么整个进程执行结束后释放。某些场景中,我们并不需要接收线程执行结束后的返回值,如果想让线程执行完后立即释放占用的私有资源,就可以通过修改 __detachstate 属性值来实现。

__detachstate 属性值用于指定线程终止执行的时机,该属性的值有两个,分别是:

  • PTHREAD_CREATE_JOINABLE:默认值,表示线程执行完函数后不会自行释放资源;
  • PTHREAD_CREATE_DETACHED:线程执行完函数后,会自行终止并释放占用的资源。

关于 __detachstate 属性,<pthread.h> 头文件中提供了 2 个与它相关的函数,分别是:

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

pthread_attr_getdetachstate() 函数用于获取 __detachstate 属性的值,detachstate 指针用于接收 __detachstate 属性的值;

pthread_attr_setdetachstate() 函数用于修改 __detachstate 属性的值,detachstate 整形变量即为新的 __detachstate 属性值。

两个函数执行成功时返回数字 0,反之返回非零数。

此外,<pthread.h> 头文件还提供有 pthread_detach() 函数,可以直接将目标线程的 __detachstate 属性改为 PTHREAD_CREATE_DETACHED,语法格式如下:

int pthread_detach(pthread_t thread);

函数执行成功时返回数字 0 ,反之返回非零数。

2) __schedpolicy

__schedpolicy 属性用于指定系统调度该线程所用的算法,它的值有以下 3 个:

  • SCHED_OTHER(默认值):分时调度算法;
  • SCHED_FIFO:先到先得(实时调度)算法;
  • SCHED_RR:轮转法;

其中,SCHED_OTHER 调度算法不支持为线程设置优先级,而另外两种调度算法支持。

<pthread.h> 头文件提供了如下两个函数,专门用于访问和修改 __schedpolicy 属性:

int pthread_attr_getschedpolicy(const pthread_attr_t *, int * policy)
int pthread_attr_setschedpolicy(pthread_attr_*, int policy)

pthread_attr_getschedpolicy() 函数用于获取当前 __schedpolicy 属性的值;

pthread_attr_setschedpolicy() 函数用于修改 __schedpolicy 属性的值。函数执行成功时,返回值为数字 0,反之返回非零数。

3) __schedparam

__scheparam 用于设置线程的优先级(默认值为 0),该属性仅当线程的 __schedpolicy 属性为 SCHED_FIFO 或者 SCHED_RR 时才能发挥作用。

<pthread.h> 头文件中提供了如下两个函数,用于获取和修改 __schedparam 属性的值:

int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);

其中,param 参数用于接收或者修改 __scheparam 属性的优先级,它是 sched_param 结构体类型的变量,定义在 <sched.h> 头文件中,内部仅有一个 sched_priority 整形变量,用于表示线程的优先级。函数执行成功时返回数字 0,反之返回非零数。

当需要修改线程的优先级时,我们只需创建一个 sched_param 类型的变量并为其内部的 sched_priority 成员赋值,然后将其传递给 pthrerd_attr_setschedparam() 函数。

不同的操作系统,线程优先级的值的范围不同,您可以通过调用如下两个系统函数获得当前系统支持的最大和最小优先级的值:

int sched_get_priority_max(int policy);   //获得最大优先级的值
int sched_get_priority_min(int policy);   //获得最小优先级的值

其中,policy 的值可以为 SCHED_FIFO、SCHED_RR 或者 SCHED_OTHER,当 policy 的值为 SCHED_OTHER 时,最大和最小优先级的值都为 0。

4) __inheritsched

新建线程的调度属性(____schedpolicy 和 __schedparam 属性)默认遵循父线程的属性(谁创建它,谁就是它的父线程),如果我们想自定义线程的调度属性,就需要借助 __inheritsched 属性。

也就是说,新线程的调度属性要么遵循父线程,要么遵循 myAttr 规定的属性,默认情况下 __inheritsched 规定新线程的调度属性遵循父线程,我们也可以修改 __inheritsched 的值,使新线程的调度属性遵循自定义的属性变量(如文章开头定义的 myAttr)规定的值。

<pthread.h> 头文件提供了如下两个函数,分别用于获取和修改 __inheritsched 属性的值:

//获取 __inheritsched 属性的值
int pthread_attr_getinheritsched(const pthread_attr_t *attr,int *inheritsched);
//修改 __inheritsched 属性的值
int pthread_attr_setinheritsched(pthread_attr_t *attr,int inheritsched);

其中在 pthread_attr_setinheritsched() 函数中,inheritsched 参数的可选值有两个,分别是:

  • PTHREAD_INHERIT_SCHED(默认值):新线程的调度属性继承自父线程;
  • PTHREAD_EXPLICIT_SCHED:新线程的调度属性继承自 myAttr 规定的值。

以上两个函数执行成功时返回数字 0,反之返回非零数。

5) __scope

线程执行过程中,可以只和同进程内的其它线程争夺 CPU 资源,也可以和系统中所有的其它线程争夺 CPU 资源,__scope 属性用于指定目标线程和哪些线程抢夺 CPU 资源。

<pthread.h> 头文件中提供了如下两个函数,分别用于获取和修改 __scope 属性的值:

//获取 __scope 属性的值
int pthread_attr_getscope(const pthread_attr_t * attr,int * scope);
//修改 __scope 属性的值
int pthread_attr_setscope(pthread_attr_t * attr,int * scope);

当调用 pthread_attr_setscope() 函数时,scope 参数的可选值有两个,分别是:

  • PTHREAD_SCOPE_PROCESS:同一进程内争夺 CPU 资源;
  • PTHREAD_SCOPE_SYSTEM:系统所有线程之间争夺 CPU 资源。

Linux系统仅支持 PTHREAD_SCOPE_SYSTEM,即所有线程之间争夺 CPU 资源。

当函数执行成功时,返回值为数字 0,反之返回非零数。

6) __stacksize

每个线程都有属于自己的内存空间,通常称为栈(有时也称堆栈、栈空间、栈内存等)。某些场景中,线程执行可能需要较大的栈内存,此时就需要我们自定义线程拥有的栈的大小。

__stacksize 属性用于指定线程所拥有的栈内存的大小。<pthread.h> 提供有以下两个函数,分别用于获取和修改栈空间的大小:

//获取当前栈内存的大小
int pthread_attr_getstacksize(const pthread_attr_t * attr,size_t * stacksize);
//修改栈内存的大小
int pthread_attr_setsstacksize(pthread_attr_t * attr,size_t * stacksize);

函数执行成功时,返回值为数字 0,反之返回非零数。

8) __guardsize

每个线程中,栈内存的后面都紧挨着一块空闲的内存空间,我们通常称这块内存为警戒缓冲区,它的功能是:一旦我们使用的栈空间超出了额定值,警戒缓冲区可以确保线程不会因“栈溢出”立刻执行崩溃。

__guardsize 属性专门用来设置警戒缓冲区的大小,<pthread.h> 头文件中提供了如下两个函数,分别用于获取和修改 __guardsize 属性的值:

int pthread_attr_getguardsize(const pthread_attr_t *restrict attr,size_t *restrict guardsize);
int pthread_attr_setguardsize(pthread_attr_t *attr ,size_t *guardsize);

pthread_attr_setguardsize() 函数中,设置警戒缓冲区的大小为参数 guardsize 指定的字节数。函数执行成功时返回数字 0,反之返回非零数。

三、代码示例

接下来通过一个样例,给大家演示如何自定义线程的属性:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
//myThread1 线程执行的函数
void *Thread1(void *arg)
{printf("Thread1 正在执行\n");printf("http://c.biancheng.net\n");printf("Thread1 执行完毕\n");return NULL;
}
//myThread2 线程执行的函数
void* Thread2(void* arg)
{printf("Thread2 正在执行\n");printf("C语言中文网\n");printf("Thread2 执行完毕\n");return NULL;
}int main(int argc,char *argv[])
{int num1, num2, res;//创建两个线程pthread_t mythread1, mythread2;//创建两个表示线程优先级的变量struct  sched_param param1, param2;//创建两个表示线程属性的变量pthread_attr_t myAttr1, myAttr2;//接收 2 个整数,用于设定线程的优先级if (argc != 3) {printf("未向程序传入 2 个表示优先级的数字\n");return 0;}//初始化线程属性res = pthread_attr_init(&myAttr1);if (res != 0) {printf("myAttr1 init Fail\n");}res = pthread_attr_init(&myAttr2);if (res != 0) {printf("myAttr1 init Fail\n");}//设置 myAttr1 的 __detachstate 属性值为 PTHREAD_CREATE_DETACHED//遵循 myAttr1 属性的线程执行函数完毕后会自行释放占用私有资源,不支持 pthread_join() 函数res = pthread_attr_setdetachstate(&myAttr1, PTHREAD_CREATE_DETACHED);if (res != 0) {printf("myAttr1 set_detachstate Fail\n");}//设置 myAttr1 的 __scope 属性值为 PTHREAD_SCOPE_SYSTEM//遵循 myAttr1 属性的线程将同系统中的所有其它线程争夺 CPU 资源res = pthread_attr_setscope(&myAttr1, PTHREAD_SCOPE_SYSTEM);if (res != 0) {printf("myAttr1 set_scope Fail\n");}//设置 myAttr2 的 __scope 属性值为 PTHREAD_SCOPE_SYSTEM//遵循 myAttr2 属性的线程将同系统中的所有其它线程争夺 CPU 资源res = pthread_attr_setscope(&myAttr2, PTHREAD_SCOPE_SYSTEM);if (res != 0) {printf("myAttr2 set_scope Fail\n");}//设置 myAttr1 的 __schedpolicy 属性值为 SCHED_FIFO//系统会以实时调用的方式执行遵循 myAttr1 属性的线程res = pthread_attr_setschedpolicy(&myAttr1, SCHED_FIFO);if (res != 0) {printf("myAttr1 set_policy Fail\n");}//设置 myAttr2 的 __schedpolicy 属性值为 SCHED_FIFO//系统会以实时调用的方式执行遵循 myAttr2 属性的线程res = pthread_attr_setschedpolicy(&myAttr2, SCHED_FIFO);if (res != 0) {printf("myAttr2 set_policy Fail\n");}//设置 myAttr1 的 __inheritsched 属性值为 PTHREAD_EXPLICIT_SCHED//myAttr1 属性的线程将遵循自定义的线程属性res = pthread_attr_setinheritsched(&myAttr1, PTHREAD_EXPLICIT_SCHED);if (res != 0) {printf("myAttr1 set_inheritsched fail\n");}//设置 myAttr2 的 __inheritsched 属性值为 PTHREAD_EXPLICIT_SCHED//myAttr2 属性的线程将遵循自定义的线程属性res = pthread_attr_setinheritsched(&myAttr2, PTHREAD_EXPLICIT_SCHED);if (res != 0) {printf("myAttr2 set_inheritsched fail\n");}//想 argv[] 数组中的字符转换为数字num1 = atoi(argv[1]);num2 = atoi(argv[2]);// 分别将 num1 和 num2 作为线程优先级的值param1.sched_priority = num1;param2.sched_priority = num2;//设置 myAttr1 属性的优先级为 param1res = pthread_attr_setschedparam(&myAttr1, &param1);if (res != 0) {printf("param1 setscheparam Fail\n");}//设置 myAttr2 属性的优先级为 param2res = pthread_attr_setschedparam(&myAttr2, &param2);if (res != 0) {printf("param2 setscheparam Fail\n");}//创建新线程并遵循 myAttr1 属性res = pthread_create(&mythread1, &myAttr1, Thread1, NULL);if (res != 0) {printf("mythread1 create Fail\n");}//创建新线程并遵循 myAttr2 属性res = pthread_create(&mythread2, &myAttr2, Thread2, NULL);if (res != 0) {printf("mythread2 create Fail\n");}sleep(5);  //等待 mythread1 和 mythread2 两个线程执行完//尝试 pthread_join() 函数等待 mythread1 线程执行结束res = pthread_join(mythread1, NULL);if (res != 0) {if (res == EINVAL) {printf("mythread1不支持调用 pthread_join()函数\n");}}//尝试等待 mythread2 线程执行结束res = pthread_join(mythread2, NULL);if (res != 0) {printf("mythread2 has finished\n");}printf("主线程执行完毕\n");return 0;
}

假设程序编写在 thread.c 文件中,执行过程如下:

[root@localhost ~]# gcc thread.c -o thread.exe -lpthread
[root@localhost ~]# ./thread.exe 30 3
Thread1 正在执行
http://c.biancheng.net
Thread1 执行完毕
Thread2 正在执行
C语言中文网
Thread2 执行完毕
mythread1不支持调用 pthread_join()函数
主线程执行完毕
[root@localhost ~]# ./thread.exe 3 30
Thread2 正在执行
C语言中文网
Thread2 执行完毕
Thread1 正在执行
http://c.biancheng.net
Thread1 执行完毕
mythread1不支持调用 pthread_join()函数
主线程执行完毕

上面展示了两组执行结果,分别为 mythread1 和 mythread2 设置了不同的优先级,从运行结果可以看到,哪个线程的优先级高(数值大),哪个线程先执行。

此外,通过程序的执行结果还可以看出,由于 mythread 线程的 __detachstate 属性为 PTHREAD_CREATE_DETACHED,因此该线程执行完 Thread1() 函数后会自行终止并释放占用的私有资源,不需要也不允许在其它线程(比如主线程)中执行 pthread_join(mythread1, NULL) 函数。

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

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

相关文章

Jenkins的环境部署,(打包、发布、部署、自动化测试)

一、Tomcat环境安装 1.安装JDK&#xff08;Java环境&#xff09; JDK下载地址&#xff1a;Java Downloads | Oracle 安装好后在系统环境变量里配置环境变量&#xff1a; ①添加JAVA_HOME 变量名&#xff1a;JAVA_HOME变量值&#xff1a;C:\Program Files\Java\jdk1.8.0_18…

前端开发 2: CSS

在前端开发中&#xff0c;CSS&#xff08;层叠样式表&#xff09;是一种用于描述网页样式的语言。它控制着网页的布局、颜色、字体等外观效果。在本篇博客中&#xff0c;我将为你介绍 CSS 的基础知识和常用技巧&#xff0c;帮助你更好地掌握前端开发中的样式设计。 CSS 基础知…

CSS笔记II

CSS第二天笔记 复合选择器后代选择器子选择器并集选择器交集选择器伪类选择器 三大特性继承性层叠性优先级优先级-叠加计算规则 Emmet写法 背景属性背景图平铺方式位置缩放固定复合属性 显示模式转换显示模式 复合选择器 定义&#xff1a;由两个或多个基础选择器&#xff0c;通…

长虹智能电视C1100i、C1080i系列、3D51V50等 ZPM41AiJ机芯 刷机方法,及刷机数据

适用机芯&#xff1a;ZPM41AiJ 适用型号&#xff1a; 3D51C1100i、3D51C1080i、3D51V50 刷机方法&#xff1a; 1、将文件下载到电脑&#xff0c;解压后有3个文件&#xff0c;3个文件放到U盘的根目录。U盘必须使用FAT32格式&#xff1b; 2、将U盘接入电视的USB接口&#…

LeetCode19:删除链表的倒数第N个结点

力扣题目链接 思路&#xff1a;由于本题有可能删除头结点&#xff0c;为保证删除头结点和其他结点的操作一致&#xff0c;因此首先创建一个虚拟头结点dummy。 其次&#xff0c;本题需要删除倒数第N个结点&#xff0c;由于单链表只有next指针&#xff0c;因此需要找到倒数第N1…

力扣精选算法100题——找到字符串中所有字母异位词(滑动窗口专题)

本题链接&#x1f449;找到字符串中所有字母异位词 第一步&#xff1a;了解题意 给定2个字符串s和p&#xff0c;找到s中所有p的变位词的字串&#xff0c;就是p是"abc",在s串中找到与p串相等的字串&#xff0c;可以位置不同&#xff0c;但是字母必须相同&#xff0c;比…

笨蛋学设计模式结构型模式-外观模式【10】

结构型模式-外观模式 7.4外观模式:arrow_up::arrow_up::arrow_up:7.4.1概念7.4.2场景7.4.3优势 / 劣势7.4.4外观模式可分为7.4.5外观模式7.4.6实战7.4.6.1题目描述7.4.6.2输入描述7.4.6.3输出描述7.4.6.4实战 7.4.7总结外观模式 7.4外观模式⬆️⬆️⬆️ 7.4.1概念 ​ 外观模…

橘子学K8S04之重新认识Docker容器

我们之前分别从 Linux Namespace 的隔离能力、Linux Cgroups 的限制能力&#xff0c;以及基于 rootfs 的文件系统三个角度来理解了一下关于容器的核心实现原理。 这里一定注意说的是Linux环境&#xff0c;因为Linux Docker (namespaces cgroups rootfs) ! Docker on Mac (bas…

JAVA开发工作笔记

Android开发 1、解决notifyDataSetChanged()刷新数据的时候&#xff0c;edittext的焦点就会自动消失 第一步&#xff1a;在adapter中复写此方法 Override public long getItemId(int position) { return position; }第二步&#xff1a;在创建Adapter的时候设置setHasStabl…

Mybatis面试题(一)

MyBatis 面试题 1、什么是 Mybatis&#xff1f; 1、Mybatis 是一个半 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了 JDBC&#xff0c;开发时只需要关注 SQL 语句本身&#xff0c;不需要花费精力去处理加载驱动、创建连接、创建statement 等繁杂的过程…

在PyCharm中创建Flask项目

在 PyCharm 中创建 Flask 项目的步骤如下&#xff1a; 打开 PyCharm&#xff0c;并选择 "Create New Project"&#xff08;新建项目&#xff09;。在弹出的窗口中&#xff0c;选择左侧的 "Python" 选项&#xff0c;然后选择右侧的 "Flask" 项目…

【开发实践】前端jQuery+gif图片实现载入界面

一、需求分析 载入界面&#xff08;Loading screen&#xff09;是指在计算机程序或电子游戏中&#xff0c;当用户启动应用程序或切换到新的场景时&#xff0c;显示在屏幕上的过渡界面。它的主要作用是向用户传达程序正在加载或准备就绪的信息&#xff0c;以及提供一种视觉上的反…

科技的成就(五十五)

519、Machine Learning "1959 年 7 月&#xff0c;塞缪尔首创 Machine Learning 一词。塞缪尔在“Some Studies in Machine Learning Using theGame of Checkers”一文中给 Machine Learning 下了个非正式定义&#xff1a;没有明确编程指令的情况下&#xff0c;能让计算机…

(C语言)冒泡排序

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//实现buble_sort函数&#xff1b; void buble_sort(int arr[], int sz) {//初始化变量值&#xff1b;int i 0;//嵌套循环冒泡排序&#xff1b;//外层循环&…

VPS与母机

VPS技术是将一台服务器分割成多个虚拟专享服务器的优质服务。实现VPS的技术分为容器技术&#xff0c;和虚拟化技术。在容器或虚 拟机中&#xff0c;每个VPS都可选配独立公网IP地址、独立操作系统、实现不同VPS间磁盘空间、内存、CPU资源、进程和系统配置的 隔离&#xff0c;为用…

HBase学习三:集群部署

集群启停顺序:启动Hadoop—>启动HBase—>关闭HBase—>关闭Hadoop 1 环境准备 1.0 环境前期准备 参考基础环境配置 1.1 机器准备 hostnameipvm1ip1vm2ip2vm3ip31.2 机器分配 hdfs的集群搭建可以参考 hdfs集群搭建 hdfs集群机器分配 NameNodeDataNodeJournalNode…

基于SSM的在线宠物商城设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue、HTML 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是…

Spring MVC学习——解决请求参数中文乱码

解决请求参数中文乱码问题 1.POST请求方式解决乱码问题 在web.xml里面设置编码过滤器 <filter><filter-name>CharacterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><…

【Java数据结构 -- 栈相关算法:中缀表达式转后缀、最小栈、括号匹配、和出栈入栈次序匹配】

栈相关算法 1.逆波兰表达式求值2. 最小栈3. 括号匹配4. 出栈入栈次序匹配 1.逆波兰表达式求值 思路&#xff1a; // 中缀 : 12*3(4*56)*7 // 后缀 : ( (1 (2*3) ) ((4*5)6)*7) ) // ( (1 (23)* ) ((45)*6)7)* ) // 1 23* 45*6 7* //给你一个字符串数组…

react native Gradle的原国外地址、本地下载、国内阿里腾讯镜像三种下载配置

一、国外地址&#xff1a;&#xff08;初始项目默认&#xff09; 下载地址&#xff1a;https://services.gradle.org/distributions/ 文件地址见下图&#xff1a; 注意&#xff1a;这个地址下载十次就有九次是连接超时&#xff0c;建议换另外两种方法 二、下载到本地&#x…