Linux系统编程教程之Linux线程函数的使用:讲解Linux线程函数

简介: Linux系统编程教程之Linux线程函数的使用:讲解Linux线程函数


本文介绍Linux下线程的常用接口


线程基本操作相关函数

  • pthread_create(创建线程)
int pthread_create(
    pthread_t *thread,                 //线程ID
    const pthread_attr_t *attr,        //线程属性
    void *(*start_routine) (void *),   //回调函数
    void *arg                        //回调函数的参数
);

参数:

thread为指向线程标识符(线程id)的指针。

attr用来设置线程属性。设NULL表示默认属性

start_routine是线程运行函数的起始地址。

arg是运行函数的参数。

返回值:

    若成功,返回0

    若失败,返回出错编号并且*thread中的内容是未定义的

  • pthread_exit(终止当前线程)
void pthread_exit(void *status);
//终止当前线程,所有绑定在线程数据键上的内存将被释放。

说明:

如果当前线程是非分离的,那么这个线程的标示符合退出代码将被保留,直到其他线程用pthread_join来等待当前线程的终止。

如果当前线程是分离的,status将被忽略,线程标示符将被立即回收。

参数:

status:不为NULL时,线程的退出代码被置为status参数指向的值。

  • pthread_cancel(退出线程)
int pthread_cancel(pthread_t thread);
//退出一个线程。如何响应退出请求取决于目标线程的状态。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • pthread_detach(分离线程)
int pthread_detach(pthread_t tid);
//将非分离的线程设置为分离线程。

说明:

即通知线程库在指定的线程终止时回收线程占用的内存等资源。

在一个线程上使用多次pthread_detach的结果是不可预见的。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • pthread_join(等待线程结束)
int pthread_join(pthread_t tid, void **status);
等待一个线程结束,该函数阻塞调用它线程,直到参数tid指定的线程结束。

说明:

不能有多个线程等待同一个线程终止。如果出现这种情况,一个线程将成功返回,别的线程将返回错误ESRCH

参数:

tid:指定的线程必须在当前进程中,同时tid指定的线程必须是非分离的。

status:不为NULL时,接收线程tid的退出状态(返回值)

返回值:

    若成功,返回0

    若失败,返回出错编号


线程属性相关函数

线程属性结构如下:

typedef struct
{
       int                               detachstate;   //线程的分离状态
       int                               schedpolicy;   //线程调度策略
       structsched_param                 schedparam;    //线程的调度参数
       int                               inheritsched;  //线程的继承性
       int                               scope;         //线程的作用域
       size_t                            guardsize;     //线程栈末尾的警戒缓冲区大小
       int                               stackaddr_set; //线程堆栈设置
       void*                             stackaddr;     //线程堆栈位置
       size_t                            stacksize;     //线程栈的大小
}pthread_attr_t;
  • 初始化/反初始化属性
int pthread_attr_init(pthread_attr_t *attr);
//把属性设置为默认值
 
int pthread_attr_destroy(pthread_attr_t *attr);
//如果初始化函数分配了与属性对象关联的资源,摧毁函数负责释放这些资源

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 获取/修改线程的分离状态属性
int pthread_attr_getdetachstate(const pthread_attr_t* attr,int* detachstate );
//从线程属性对象attr获取线程分离状态属性,并在detachstate中返回它。
 
int pthread_attr_setdetachstate(pthread_attr_t* attr,int detachstate );
//将线程属性对象attr中的线程分离状态属性设置为detachstate。

参数:

attr:指向pthread_attr_t结构的指针,用于定义创建新线程时要使用的属性。

detachstate:指向一个位置的指针,该函数可以存储线程分离状态:

PTHREAD_CREATE_JOINABLE     - 以可连接状态创建线程(默认值)。

PTHREAD_CREATE_DETACHED   - 在分离状态下创建线程。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 获取/设置线程创建栈属性
int pthread_attr_getstack(const pthread_attr_t *restrict attr,void **restrict stackaddr, size_t *restrict stacksize);
//获取线程创建栈属性stackaddr和stacksize。
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr,size_t stacksize);
//设置线程创建栈属性stackaddr和stacksize。

参数:

stackaddr :堆栈属性指定要用于创建的线程堆栈的存储区域的基地址(最低可寻址字节)

(stackaddr和0x7)不为0,pthread_attr_setstack()可能会失败[EINVAL]。

stacksize:存储器的大小,应至少为{PTHREAD_STACK_MIN}。堆叠堆栈应适当对齐,用作堆叠;

stackaddr和stacksize描述的堆栈内的所有页面都应该是线程可读写的。

返回值:

    若成功,返回 0 , pthread_attr_getstack()函数将堆栈属性值存储在stackaddr和stacksize中。

    若失败,返回出错编号

  • 获取/设置线程栈大小属性
int pthread_attr_getstacksize(const pthread_attr_t* attr,size_t* stacksize );
//从线程属性对象attr获取线程栈大小属性,并在stacksize中返回它。
 
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

说明:

堆栈溢出保护可以浪费系统资源。创建许多线程的应用程序可以通过关闭保护区域来保护系统资源,如果它信任其线程不会溢出堆栈。

当线程在堆栈上分配大的对象时,需要大的保护来检测堆栈溢出。

参数:

attr :指向pthread_attr_t结构的指针,用于定义创建新线程时要使用的属性。

stacksize:指向一个位置的指针,该函数可以存储要用于新线程的堆栈大小。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 获取/设置线程的保护区大小
int pthread_attr_getguardsize(const pthread_attr_t* attr,size_t* guardsize );
//从属性结构attr获取线程守护属性的值。
 
int pthread_attr_setguardsize(pthread_attr_t* attr, size_t guardsize );
//将属性结构attr中线程的保护区域的大小设置为guardsize。

说明:

该防区有助于防止堆叠溢出;在堆栈的溢出端分配额外内存的保护字节。

如果一个线程溢出到这个缓冲区,它会接收一个SIGSEGV信号。

参数:

attr :指向pthread_attr_t结构的指针,用于定义创建新线程时要使用的属性。

guardsize:控制着线程栈末尾之后用以避免栈溢出的扩展内存大小。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 获取/设置线程的继承性
int pthread_attr_getinheritsched(const pthread_attr_t*attr,int *inheritsched);
 
int pthread_attr_setinheritsched(pthread_attr_t *attr,intinheritsched);
 
//继承性决定调度的参数是从创建的进程中继承还是使用在schedpolicy和schedparam属性中显式设置的调度信息。

说明:

Pthreads不为inheritsched指定默认值,因此如果你关心线程的调度策略和参数,必须先设置该属性。

参数:

attr:指向属性对象的指针,

inheritsched:继承性或指向继承性的指针。

继承性的可能值是

PTHREAD_INHERIT_SCHED(表示新现成将继承创建线程的调度策略和参数)

PTHREAD_EXPLICIT_SCHED(表示使用在schedpolicy和schedparam属性中显式设置的调度策略和参数)。

如果你需要显式的设置一个线程的调度策略或参数,那么你必须在设置之前将inheritsched属性设置PTHREAD_EXPLICIT_SCHED

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 取/设置线程的调度策略
int pthread_attr_getschedpolicy(const pthread_attr_t*attr,int *policy);
 
int pthread_attr_setschedpolicy(pthread_attr_t *attr,intpolicy);

参数:

attr:指向属性对象的指针

intpolicy:调度策略或指向调度策略的指针。

调度策略可能的值是

  1. 先进先出(SCHED_FIFO)
  2. 轮转法(SCHED_RR)
  3. 其它(SCHED_OTHER)

SCHED_FIFO策略允许一个线程运行直到有更高优先级的线程准备好,或者直到它自愿阻塞自己。

在SCHED_FIFO调度策略下,当有一个线程准备好时,除非有平等或更高优先级的线程已经在运行,否则它会很快开始执行。

SCHED_RR(轮循)策略是基本相同的,不同之处在于:

如果有一个SCHED_RR策略的线程执行了超过一个固定的时期(时间片间隔)没有阻塞,而另外的SCHED_RR或SCHBD_FIPO策略的相同优先级的线程准备好时,运行的线程将被抢占以便准备好的线程可以执行。

返回值:

    若成功,返回0

    若失败,返回出错编号

说明:

当有SCHED_FIFO或SCHED_RR策赂的线程在一个条件变量上等持或等持加锁同一个互斥量时,它们将以优先级顺序被唤醒。

即,如果一个低优先级的SCHED_FIFO线程和一个高优先级的SCHED_FIFO线程都在等待锁相同的互斥,

则当互斥量被解锁时,高优先级线程将总是被首先解除阻塞。

  • 调度参数得到线程可设置的最大最低优先级
int sched_get_priority_max(int policy);
int sched_get_priority_min(int policy);

SCHED_OTHER 是不支持优先级使用的,它最大和最小优先级都是0。
SCHED_FIFO SCHED_RR 支持优先级的使用,他们分别为1和99,数值越大优先级越高 。

  • 获取/设置线程的调度参数
int pthread_attr_getschedparam(const pthread_attr_t*attr,struct sched_param *param);
//修改线程的优先级。
int pthread_attr_setschedparam(pthread_attr_t *attr,conststruct sched_param *param);
//获取线程的优先级。

参数:

attr:指向属性对象的指针

param:sched_param结构或指向该结构的指针。    

结构sched_param在文件/usr/include/bits/sched.h中定义如下:

struct sched_param
{
       intsched_priority;
};

结构sched_param的子成员sched_priority控制一个优先权值,大的优先权值对应高的优先权。系统支持的最大和最小优先权值可以用sched_get_priority_max函数和sched_get_priority_min函数分别得到。

返回值:

    若成功,返回0

    若失败,返回出错编号

说明:

如果不是编写实时程序,不建议修改线程的优先级。

因为,调度策略是一件非常复杂的事情,如果不正确使用会导致程序错误,从而导致死锁等问题。

如:在多线程应用程序中为线程设置不同的优先级别,有可能因为共享资源而导致优先级倒置。

  • 线程的作用域
 
#include <pthread.h>   
int    pthread_attr_getscope( const pthread_attr_t * attr, int * scope );
int    pthread_attr_setscope( pthread_attr_t*, int scope );

/*************************************
作用域控制线程是否在进程内或在系统级上竞争资源,可能的值是
PTHREAD_SCOPE_PROCESS(进程内竞争资源)
PTHREAD_SCOPE_SYSTEM   (系统级竞争资源)。
/**************************************


线程特定数据相关函数

  • 创建线程键
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
//在进程中分配一个键值,这个键被用来表示一个线程数据项。

说明:

这个键对进程中所有的线程都是可见的。刚创建线程数据键时,在所有线程中和这个键相关联的值都是NULL。

函数成功返回后,分配的键放在key参数指向的内存中,必须保证key参数指向的内存区的有效性。

如果指定了解析函数destructor,那么当线程结束时并且将非空的值绑定在这个键上,系统将调用destructor函数,参数就是相关线程与这个键绑定的值。绑定在这个键上的内存块可由destructor函数释放。

参数:

key: pthread_key_t 变量

destructor:一个清理函数(析构函数),用来在线程释放该线程存储的时候被调用。该函数指针可以设成 NULL ,这样系统将调用默认的清理函数。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 销毁线程键
int pthread_key_delete(pthread_key_t key);
//销毁线程特定数据键。由于键已无效,因此将释放与该键关联的所有内存。

说明:

在调用该函数之前必须释放所有线程的特定资源,该函数不会调用任何析构函数。

反复调用pthread_key_create与pthread_key_delete可能会产生问题。

对于每个所需的键,应当只调用pthread_key_create一次。

参数:

key:需要删除的键

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 线程特定的数据管理
void *pthread_getspecific(pthread_key_t key);
//获得线程特定数据的地址, 将返回当前绑定到指定键的值代表调用线程。
int pthread_setspecific(pthread_key_t key, const void *value);
// 把键和线程特定数据相关联

说明:

将线程特定值与通过先前调用pthread_key_create()获取的密钥相关联。

不同的线程可以将不同的值绑定到相同的密钥。

这些值通常指向已被保留供调用线程使用的动态分配的存储器的块的指针。

未使用pthread_key_delete()删除pthread_key_create()或key之后的键值的pthread_getspecific()或pthread_setspecific()的函数已被删除的效果。

pthread_getspecific()和pthread_setspecific()可以从线程特定的数据析构函数调用。

对被破坏的线程特定数据密钥的pthread_getspecific()的调用将返回值NULL,除非通过调用pthread_setspecific()来更改该值(在析构函数启动之后)。

从线程特定的数据析构函数调用pthread_setspecific()可能会导致丢失的存储(至少PTHREAD_DESTRUCTOR_ITERATIONS尝试破坏)或无限循环。

这两个功能都可以实现为宏。

返回值:

pthread_getspecific:

  pthread_getspecific()函数将返回与给定键相关联的线程特定数据值。

  如果没有线程特定的数据值与密钥相关联,则返回值NULL

pthread_setspecific:    

   若成功,pthread_setspecific()函数将返回0;

   若失败,返回出错编号

  • 动态包初始化
int pthread_once(pthread_once_t *once_control,void (*init_routine)(void));
//使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次

说明:

从pthread_once()返回时,init_routine应该已经完成了。

 如果已经有线程通过pthread_once调用过这个初始化函数一次,那么以后通过pthread_once函数再调用这个初始化函数将无效。

参数:

once_control: 决定是否调用了相关联的初始化例程。

init_routine:必须是一个非本地变量,而且必须初始化为PTHREAD_ONCE_INIT。

返回值:

    若成功,返回0

    若失败,返回出错编号


线程调度相关函数

  • 出让执行权
int sched_yield(void);
//导致调用线程放弃CPU。该线程被移动到队列的末尾以获得其静态优先级和a新线程运行。

参数:  

     sig:若为0,则不发送任何信号,但仍然执行错误检查; 这可以用来检查线程ID的存在。

返回值:

    若成功,返回0

    若失败,返回-1,并设置errno

  • 修改/获取优先级
int pthread_setschedparam(pthread_t tid, int policy, const struct sched_param *param);
//修改线程的优先权。
int pthread_getschedparam(pthread_t tid, int policy, struct schedparam *param);
//获取线程的优先级。

返回值:

    若成功,返回0

    若失败,返回-1,并设置errno


线程信息相关函数

  • 比较线程
int pthread_equal(pthread_t tid1, pthread_t tid2);

说明:

如果tid1和tid2相同,函数返回一个非0值,否则返回0。

如果tid1或tid2中任何一个是非法值,则返回将是不可预料的。

返回值:

    若两个线程ID相等,返回非0值

    若不相等,则返回0

  • 获取线程标示符
pthread_t pthread_self(void);
//返回当前线程的标示符。
  • 设置线程掩码
int pthread_sigmask(int how, const sigset_t *new, sigset_t *old);
//改变或检验当前线程的信号掩码。

参数:

how:表示对当前信号掩码进行什么操作,有如下值:SIG_BLOCK、SIG_UNBLOCK、SIG_SETMASK

new:当参数new为NULL时,不论how的值是什么,当前线程的信号掩码都不会改变。

old:旧的信号掩码保存在参数old指向的内存中,当old不为NULL时。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 设置可取消状态(允许/禁止退出线程)
int pthread_setcancelstate(int state,int * oldstate);
//原则上将调用线程的可取消状态设置为指示状态,并在oldstate引用的位置返回先前的可取消状态。 

参数:

state:状态的合法值为

PTHREAD_CANCEL_ENABLE(启动时的默认可取消状态):线程将在下一个取消点上对所有挂起的取消请求进行处理

PTHREAD_CANCEL_DISABLE :对pthread_cancel的调用并不会杀死线程,相反,取消请求对这个线程来说还处于挂起状态。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 设置可取消类型(退出类型)
int pthread_setcanceltype(int type,int * oldtype);
 
//原则上将调用线程的可取消类型设置为指定的类型,并在oldtype引用的位置返回先前的可取消类型。 

参数:

type:类型的合法值为

PTHREAD_CANCEL_DEFERRED(默认值)PTHREAD_CANCEL_ASYNCHRONOUS

当一个线程被创建后,缺省值是延迟类型。在异步方式下,线程可以在执行的任何时候被退出。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 创建一个取消点
void pthread_testcancel(void);
//将在调用线程中创建一个取消点。 

说明:

只有当线程的退出状态是允许退出的,而且线程的退出类型是延迟时,调用该函数才有效。

如果调用时线程的退出状态是禁止的,则该调用不起作用。小心使用该函数,只有在能够安全的被退出的地方才能够设置退出点

  • 压入/弹出善后处理函数
void pthread_cleanup_push(void (*routine)(void *), void *args);
//将一个善后处理函数压入善后处理函数堆栈。
 
void pthread_cleanup_pop(int execute);
//从善后处理函数堆栈中弹出一个善后处理函数。如果参数execute非0,则执行弹出的函数;如果参数为0,则不执行弹出函数。
 

如果一个线程显式或隐式的调用pthread_exit()函数或线程接受了退出请求,线程库实际上将会以非0参数调用pthread_cleanup_pop函数。

  • 发送信号
int pthread_kill(pthread_t tid, int sig);
//向tid指定的线程发送一个信号

参数:

tid:指定的线程必须和当前线程在同一个进程中。

sig:参数为0时,函数将进行错误检查,不发送信号,这常常用来检查tid的合法性。

返回值:

    若成功,返回0

    若失败,返回出错编号


std::thread 的使用

目录
相关文章
|
5天前
|
安全 Java 调度
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第12天】 在现代软件开发中,多线程编程是提升应用程序性能和响应能力的关键手段之一。特别是在Java语言中,由于其内置的跨平台线程支持,开发者可以轻松地创建和管理线程。然而,随之而来的并发问题也不容小觑。本文将探讨Java并发编程的核心概念,包括线程安全策略、锁机制以及性能优化技巧。通过实例分析与性能比较,我们旨在为读者提供一套既确保线程安全又兼顾性能的编程指导。
|
5天前
|
安全 前端开发 程序员
|
1天前
|
Java
深入理解Java并发编程:线程池的应用与优化
【5月更文挑战第18天】本文将深入探讨Java并发编程中的重要概念——线程池。我们将了解线程池的基本概念,应用场景,以及如何优化线程池的性能。通过实例分析,我们将看到线程池如何提高系统性能,减少资源消耗,并提高系统的响应速度。
12 5
|
2天前
|
消息中间件 安全 Java
理解Java中的多线程编程
【5月更文挑战第18天】本文介绍了Java中的多线程编程,包括线程和多线程的基本概念。Java通过继承Thread类或实现Runnable接口来创建线程,此外还支持使用线程池(如ExecutorService和Executors)进行更高效的管理。多线程编程需要注意线程安全、性能优化和线程间通信,以避免数据竞争、死锁等问题,并确保程序高效运行。
|
2天前
|
安全 Java 容器
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第18天】随着多核处理器的普及,并发编程变得越来越重要。Java提供了丰富的并发编程工具,如synchronized关键字、显式锁Lock、原子类、并发容器等。本文将深入探讨Java并发编程的核心概念,包括线程安全、死锁、资源竞争等,并分享一些性能优化的技巧。
|
2天前
|
安全 Java 开发者
Java中的多线程编程:理解与实践
【5月更文挑战第18天】在现代软件开发中,多线程编程是提高程序性能和响应速度的重要手段。Java作为一种广泛使用的编程语言,其内置的多线程支持使得开发者能够轻松地实现并行处理。本文将深入探讨Java多线程的基本概念、实现方式以及常见的并发问题,并通过实例代码演示如何高效地使用多线程技术。通过阅读本文,读者将对Java多线程编程有一个全面的认识,并能够在实际开发中灵活运用。
|
3天前
|
监控 Java 测试技术
在多线程开发中,线程死循环可能导致系统资源耗尽,影响应用性能和稳定性
【5月更文挑战第16天】在多线程开发中,线程死循环可能导致系统资源耗尽,影响应用性能和稳定性。为解决这一问题,建议通过日志记录、线程监控工具和堆栈跟踪来定位死循环;处理时,及时终止线程、清理资源并添加错误处理机制;编码阶段要避免无限循环,正确使用同步互斥,进行代码审查和测试,以降低风险。
18 3
|
4天前
|
安全 Java 开发者
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第15天】本文将深入探讨Java并发编程的核心概念,包括线程安全和性能优化。我们将通过实例分析,理解线程安全的重要性,并学习如何通过各种技术和策略来实现它。同时,我们也将探讨如何在保证线程安全的同时,提高程序的性能。
|
5天前
|
JSON 前端开发 安全
【个人博客系统网站】我的博客列表页 · 增删改我的博文 · 退出登录 · 博客详情页 · 多线程应用(下)
【个人博客系统网站】我的博客列表页 · 增删改我的博文 · 退出登录 · 博客详情页 · 多线程应用
8 0
|
5天前
|
JSON 前端开发 安全
【个人博客系统网站】我的博客列表页 · 增删改我的博文 · 退出登录 · 博客详情页 · 多线程应用(上)
【个人博客系统网站】我的博客列表页 · 增删改我的博文 · 退出登录 · 博客详情页 · 多线程应用
9 0