`

线程属性

阅读更多
    在线程基础函数一节中我们曾提到在调用 pthread_create 函数时可以指定线程属性,还可以用 pthread_detach 函数来分离线程,以让操作系统在线程退出时收回它所占用的资源。现在就是深入讨论这个话题的时候。
    可以使用 pthread_attr_t 结构修改线程默认属性,并把这些属性与创建的线程联系起来。
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
                     /* 两个函数的返回值:若成功,返回 0;否则,返回错误编号 */

    pthread_attr_init 函数可以用来初始化 pthread_attr_t 结构。调用该函数后,线程属性结构包含的就是操作系统支持的所有线程属性的默认值。pthread_attr_destroy 函数则可以释放动态分配的属性对象的内存空间,还会用无效的值初始化属性对象。
    下表总结了 POSIX.1 定义的线程属性以及各个操作系统平台的支持情况。

    如果在创建线程时就知道不需要了解线程的终止状态,就可以使用 pthread_attr_t 结构中的 detachstate 线程属性,让线程一开始就处于分离状态。pthread_attr_setdetachstate 函数可以把线程属性设置成以下两个合法值之一:PTHREAD_CREATE_DETACHED,以分离状态启动线程;或者 PTHREAD_CREATE_JOINABLE,正常启动线程,此时应用程序可以获取线程的终止状态。函数 pthread_attr_getdetachstate 则可用来获取当前的 detachstate 线程属性。
#include <pthread.h>
int pthread_attr_getdetachstate(const pthread_attr_t *restrict attr, int *detachstate);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int *detachstate);
                     /* 两个函数的返回值:若成功,返回 0;否则,返回错误编号 */

    下面给出了一个以分离状态创建线程的函数。
#include <pthread.h>

int makeDetachThread(void *(*fn)(void *), void *arg){
	pthread_t	tid;
	pthread_attr_t	attr;
	int err = pthread_attr_init(&attr);
	if(err != 0)
		return err;
	err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
	if(err == 0)
		err = pthread_create(&tid, &attr, fn, arg);
	pthread_attr_destroy(&attr);
	return err;
}

    注意,这里忽略了 pthread_attr_destroy 的返回值,以免覆盖 pthread_create 的返回值。因此万一该函数的清理工作失败,则可能会有少量的内存泄漏。
    遵循 POSIX 标准的系统不一定支持线程栈属性,可以在编译阶段使用 _POSIX_THREAD_ATTR_STACKADDR 和 _POSIX_THREAD_ATTR_STACKSIZE 符号或者在运行阶段把 _SC_THREAD_ATTR_STACKADDR 和 _SC_THREAD_ATTR_STACKSIZE 参数传给 sysconf 函数来检查系统是否支持每一个线程栈属性。
    下面是一组可用来操作线程栈属性的函数。
#include <pthread.h>
int pthread_attr_getstack(const pthread_attr_t *restrict attr,
            void **restrict stackaddr, size_t *restrict stacksize);
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);

int pthread_attr_getstacksize(const pthread_attr_t *restrict attr,
            size_t *restrict stacksize);
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

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);
                     /* 所有函数的返回值:若成功,返回 0;否则,返回错误编号 */

    对于进程来说,虚地址空间的大小是固定的,因为进程中只有一个栈,所以它的大小通常不是问题。但对于线程来说,同样大小的虚地址空间必须被所有的线程栈共享。如果这些线程栈的累计大小超过了可用的虚地址空间,就需要减少默认的线程栈大小。而如果线程调用的函数分配了大量的自动变量,或者涉及许多很深的栈帧,那么所需的栈大小又可能要比默认的大。如果线程栈的虚地址空间都用完了,则可以使用 malloc 或 mmap 来为可替代的栈分配空间,并用 pthread_attr_setstack 函数来改变新建线程的栈位置。其中 stackaddr 参数指定的地址可以用作线程栈的内存范围中的最低可寻址地址(但不一定是栈的开始位置,因为如果栈是从高地址向低地址增长的,则 stackaddr 将是栈的结尾位置),该地址与处理器结构相应的边界应对齐(假设 malloc 和 mmap 所用的虚地址范围与线程栈当前使用的虚地址范围不同)。
    pthread_attr_getstacksize 和 pthread_attr_setstacksize 函数可以读取和设置线程属性 stacksize。如果希望改变默认的栈大小,但又不想自己处理线程栈的分配问题,这时使用 pthread_attr_setstacksize 函数就很有用。不过设置 stacksize 属性时,选择的 stacksize 不能小于 PTHREAD_STACK_MIN。
    线程属性 guardsize 控制着线程栈末尾之后用以避免栈溢出的扩展内存的大小。该属性默认值是由具体实现来定义的,但常用值是系统页大小。可以把 guardsize 属性设置为 0,这样就不会提供警戒缓冲区。如果修改了 stackaddr 属性,系统也会认为我们将自己管理栈,进而使警戒缓冲区机制无效。如果 guardsize 属性被修改了,操作系统可能会把它取为页大小的整数倍。如果线程的栈指针溢出到警戒区域,应用程序就可能通过信号接收到出错信息。

    不过有两个线程属性并没有包含在 pthread_attr_t 结构中,它们是可取消状态和可取消类型。它们影响着线程在响应 pthread_cancel 函数调用时所呈现的行为。
    可取消状态属性可以是 PTHREAD_CANCEL_ENABLE 或 PTHREAD_CANCEL_DISABLE。线程可以调用 pthread_setcancelstate 来修改它的可取消状态。
#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);
                         /* 返回值:若成功,返回 0;否则,返回错误编号 */
void pthread_testcancel(void);

    pthread_setcancelstate 把当前的可取消状态设置为 state,并把原来的可取消状态存储在 oldstate 指向的内存单元中,这两步是一个原子操作。
    因为 pthread_cancel 不会等待线程终止,所以默认情况下,线程在取消请求发出后会继续运行,直到线程到达某个取消点。取消点是线程检查它是否被取消的一个位置,如果取消了,则按照请求行事。POSIX.1 保证取消点在线程调用下图中的任何函数时都会出现。

    线程默认是可取消的,当可取消状态设为 PTHREAD_CANCEL_DISABLE 时,调用 pthread_cancel 并不会杀死线程。相反,取消请求对这个线程来说还处于挂起状态。当取消状态再次变为 PTHREAD_CANCEL_ENABLE 时,线程将在下一个取消点上对所有挂起的取消请求进行处理。
    除了上图中的函数,POSIX.1 还指定了下图中的函数作为可选的取消点。

    如果应用程序在很长的一段时间内都不会调用上面两个图中的函数,也可以调用 pthread_testcancel 函数在程序中添加自己的取消点。调用该函数时,如果有某个取消请求正处于挂起状态,而且取消并没有置为无效,那么线程就会被取消,否则将不会有任何效果。
    上面描述的默认的取消类型也称为推迟取消,调用 pthread_cancel 后,在线程到达取消点之前,并不会出现真正的取消。可以通过 pthread_setcanceltype 来修改取消类型。
#include <pthread.h>
int pthread_setcanceltype(int type, int *oldtype);
                         /* 返回值:若成功,返回 0;否则,返回错误编号 */

    type 参数可以是 PTHREAD_CANCEL_DEFERRED 或 PTHREAD_CANCEL_ASYNCHRONOUS,该函数把取消类型设置为 type,并把原来的取消类型保存到 oldtype 指向的内存单元中。使用异步取消时,线程就可以在任意时间撤销了。
  • 大小: 9 KB
  • 大小: 11.1 KB
  • 大小: 58.6 KB
分享到:
评论

相关推荐

    线程属性理解源码——属性、释放线程

    线程属性包括但不限于: 1. **线程优先级**:操作系统根据线程的优先级决定哪个线程优先获得CPU资源。不同的系统有不同的优先级范围,例如在Unix/Linux中,可以使用nice值来设置线程优先级。 2. **线程同步与互斥*...

    Posix多线程编程—线程属性.doc

    线程属性是线程管理的关键部分,它们定义了线程的行为特性,如线程的分离状态、调度策略等。本篇文章将深入探讨Posix线程属性的细节。 首先,线程属性由`pthread_attr_t`结构体表示,使用前需要初始化,通常通过`...

    线程属性(linux内核)1

    本章主要讨论的是如何通过线程属性对象来定制线程的创建和行为,特别是针对Linux环境。 线程属性对象是一个重要的概念,它允许开发者指定不同于默认行为的线程属性。这些属性可以在创建线程时通过`pthread_create...

    C例子:线程属性

    该程序是我写的博客“一起talk C栗子吧(第一百二十回:C语言实例--线程属性)”的配套程序,共享给大家使用

    C语言线程调度与优先级配置示例

    通过配置线程调度策略与优先级,可使各线程按照设定的顺序执行,从而达到线程间同步的目的,线程属性配置函数包括: pthread_attr_init(&attr[i]);// 初始化线程属性 pthread_attr_setschedpolicy(&attr[i], SCHED...

    C++ 如何正确的使用线程 释放线程资源

    在创建joinable状态的线程时,可以通过标准的pthread_create()函数来完成,并传入PTHREAD_CREATE_JOINABLE作为线程属性。完成后,如果需要等待该线程结束,必须调用pthread_join()函数。以下是示例代码: ```c++ #...

    Linux线程属性总结

    线程属性标识符:pthread_attr_t 包含在 pthread.h 头文件中。 //线程属性结构如下: typedef struct { int etachstate; //线程的分离状态 int schedpolicy; //线程调度策略 structsched_param ...

    Delphi7创建及释放线程实例

    2. **设置线程属性:** - `Suspended`属性控制线程是否在创建后立即启动。如果设为True,线程将被暂停,直到调用`Resume`方法。 - `FreeOnTerminate`属性决定线程结束后是否自动释放。如果设为True,线程对象将在...

    多线程编程指南.pdf

    - 清除不再需要的线程属性对象。 **设置分离状态** - 配置线程是否自动分离。 **获取分离状态** - 查询线程的分离状态。 **设置栈溢出保护区大小** - 设定线程栈溢出时的安全区大小。 **获取栈溢出保护区大小...

    linux多线程设计及示例

    这个函数需要四个参数:线程标识符的指针,线程属性(默认为NULL,表示使用默认属性),线程执行的函数指针,以及传递给线程函数的参数。创建成功时,函数返回0。 2. **线程等待** `pthread_join`函数用于等待一个...

    linux多线程编程.pdf

    2. 线程的创建:pthread_create() 函数用于创建一个新线程,它的参数包括一个指向pthread_t型变量的指针(用来存储新线程的标识符)、指向线程属性对象的指针(通常传递NULL,使用默认属性)、一个返回类型为void*的...

    利用VB6实现多线程

    2. 设定线程属性 在VB6的工程属性中,找到“每个对象对应一个线程”选项,并将其勾选。这样,每一个控件或对象都会在自己的线程上运行,实现真正的多线程。 3. 编写线程代码 在ActiveX EXE工程中编写线程要执行的...

    LINUX线程函数大全.pdf

    LINUX 线程函数大全是一个详细的线程函数手册,涵盖了线程创建、线程属性、线程结束、线程等待、线程分离、线程数据等多方面的知识点。本手册将详细讲解每个线程函数的使用方法、参数设置、返回值等信息,从而帮助...

    Linux多线程学习

    本学习资料详细介绍了Linux环境下的多线程编程,涵盖了线程的创建与退出、线程属性设置、以及两种主要的线程同步机制——互斥锁和信号量。 首先,我们来了解一下线程的创建和退出。在Linux中,创建线程主要通过`...

    由浅入深Linux下pthread线程库介绍[归类].pdf

    本文将详细介绍Linux下pthread线程库的基本概念、线程创建、线程结束、线程属性修改等方面的知识点。 线程的优点 多线程程序作为一种多任务、并发的工作方式,有以下的优点: 1. 提高应用程序响应:使用多线程...

Global site tag (gtag.js) - Google Analytics