文档管理中心
我的

Linux C++线程之pthread的基本使用


背景简介


在C++开发中,原生的线程库主要有两个,一个是C++11提供的<thread>(std::thread类),另一个是Linux下的<pthread.h>(p_thread类),本文主要介绍pthread的基本使用方式,假设读者对线程基础知识已有了解。


pthread中的p是POSIX的缩写,而POSIX是Portable Operating System Interface的缩写,是IEEE为要在各种UNIX操作系统上运行软件,而定义API的一系列互相关联的标准的总称。(Windows环境下无pthread,Linux GCC4.6以下编译需加-pthread编译选项)


std::thread是C++11中的新特性,将多线程程序的编写提升到了语言层面,使得编写的多线程程序的可移植性大大提高。(安卓NDK的GCC4.6以下不包含std::thread,如需使用需要额外配置编译方案)


对二者特性对比,用一句话简单概括就是:std::thread更加简便易用,而pthread功能更加强大。


pthread基本使用


引用头文件#include <pthread.h>


以下所介绍的函数大多返回值为int,它返回的是函数执行的结果状态,0表示成功,其他值表示不同类型的失败,在下文中不再一一介绍。


线程句柄 pthread_t:


这是pthread中一个线程的句柄类型(句柄可以理解为ID),它其实是一个unsigned long int,用于标识一个线程。需要注意不可将其当做数值进行任何运算操作,应当使用pthread_equal()来比较两个线程句柄,使用pthread_self()来获取自身句柄。


线程属性 pthread_attr_t:


线程属性pthread_attr_t内包括scope属性,detach属性,堆栈地址,堆栈大小,优先级。属性的设置和获取通过一系列的set和get方法。

其结构定义如下:


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)


在设置pthread_attr_t之前,应先使用此方法来对pthread_attr_t进行初始化,之后方可设置。

设置scope属性 - int pthread_attr_setscope(pthread_attr_t* attr, int scope)


   scope属性表示线程的竞争范围,有两个可选值,PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。


设置detachstate属性 - int pthread_attr_setdetachstate (pthread_attr_t* attr, int detachstate)


   detachstate表示新线程是否与进程中的其他线程脱离同步,这决定了线程结束时如何释放自身的资源。该属性默认为PTHREAD_CREATE_JOINABLE,这种情况下,线程结束后并不会自己释放自己持有的资源,而是保持结束时的状态。他的资源必须由创建他的线程来释放,必须使用pthread_join()来使创建它的线程来释放他的资源。该值也可设置为PTHREAD_CREATE_DETACHED,或在执行是使用pthread_detach()来设置也是一样的,这种情况下,线程处于分离状态,当线程执行结束后他将自己释放自己的资源。这个设置是不可逆的,不能再转回为PTHREAD_CREATE_JOINABLE。

   

设置schedparam属性 -  int pthread_attr_setschedparam (pthread_attr_t* attr, struct sched_param* param)


  schedparam是调度相关的属性,输入的是一个sched_param结构体的指针,该结构体定义如下:


struct sched_param

{

   int32_t  sched_priority;

   int32_t  sched_curpriority;

   union

   {

       int32_t  reserved[8];

       struct

       {  

           int32_t  __ss_low_priority;

           int32_t  __ss_max_repl;

           struct timespec     __ss_repl_period;  

           struct timespec     __ss_init_budget;  

       }           __ss;  

   }           __ss_un;  

}

#define sched_ss_low_priority   __ss_un.__ss.__ss_low_priority

#define sched_ss_max_repl       __ss_un.__ss.__ss_max_repl

#define sched_ss_repl_period    __ss_un.__ss.__ss_repl_period

#define sched_ss_init_budget    __ss_un.__ss.__ss_init_budget


该结构体中常用的是int sched_priority,用于设置优先级,通常我们只更改这个属性就够了,其他在此不做详细介绍。


  该属性的设置也可以使用int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched)来设置,设置值为PTHREAD_INHERIT_SCHED使得线程继承创建线程的调度属性,自身设置的属性将被忽略,若该值设为PTHREAD_EXPLICIT_SCHED则使用自身的调度属性设置。


设置schedpolicy属性 - void pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);


   该属性定义了调度策略,其值有:

SCHED_FIFO:先进先出调度策略,执行线程运行到结束。该策略下sched_param唯一有效成员int sched_priority。

SCHED_RR:轮询调度策略,按照时间片将每个线程分配到处理器上。该策略下sched_param唯一有效成员int sched_priority。

SCHED_OTHER:另外的调度策略(根据实现定义)。这是任何新创建线程的默认调度策略。


设置线程栈的地址 - int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);


该函数输入的第二个参数为栈的起始地址,第三个参数为栈的大小。可以使用get方法来获取这两个值int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize)。


设置线程警戒缓冲区 - int pthread_attr_setguardsize(pthread_attr_t* attr ,size_t* guardsize);


   警戒缓冲区在栈的末尾,防止栈溢出,当指针进入这段区域时,系统会发出错误信号给线程。该值默认为PAGESIZE的大小,且应设为PAGESIZE大小的整数倍,若不是,系统会自动修改为整数倍。


创建线程


int pthread_create(pthread_t *, const pthread_attr_t *,void *(*)(void *), void *)


第一个参数是创建的线程的句柄指针,第二个参数是线程的属性结构体的指针,


第三个参数是一个函数指针,即线程所要执行的函数,第四个参数是一个指针,指向函数所需的参数。


创建线程会按照设置的参数去创建,如果使用完全默认的设置,第二个参数可以设为NULL。若函数不需要参数,则第四个参数也可以设为NULL


线程一旦创建成功,会立即开始执行线程的函数。


线程退出


void pthread_exit(void *retval)

该函数的参数为线程的返回值,线程退出后,返回值可由pthread_join (pthread_t th,void **thread_return)来获取。这个函数只能由线程自身来调用。


线程取消


int pthread_cancel(pthread_t thread);


   该函数与线程退出的不同之处在于,它由一个线程调用,去要求另一个线程退出,输入的参数是要退出的线程的句柄。


   一个线程可以通过自己设置来允许或拒绝被取消,int pthread_setcancelstate(int state,int *oldstate),设置值为PTHREAD_CANCEL_ENABLE允许被取消,PTHREAD_CANCEL_DISABLE不允许被取消。


   如果线程允许被取消,还可以进一步设置取消策略,int pthread_setcanceltype(int type,int *oldstate),设置为PTHREAD_CANCEL_ASYNCHORONOUS则收到取消请求后立即执行,PTHREAD_CANCEL_DEFERRED在收到取消请求后先执行以下几个函数之一:pthread_join、pthread_cond_wait、pthread_cond_timewait、pthread_testcancel、sem_wait或sigwait。


线程同步


pthread_mutex_t 互斥量类型


一个mutex必须仙贝初始化,可将其赋值为PTHREAD_MUTEX_INITIALIZER或使用int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr)。销毁一个mutex可以使用int pthread_mutex_destroy(pthread_mutex_t *mutex)。


mutex的加锁解锁方法有以下,顾名思义,不做具体介绍:


int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread_mutex_t *mutex);


pthread_rwlock_t 读写锁类型


读写锁也必须初始化后才能使用,int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr),其销毁函数为int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)。


rwlock的加锁解锁方法有以下:


int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);


pthread_cond_t 线程同步相关条件


pthread_cond_t必须初始化,int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condxattr_t *restrict attr),销毁方法int pthread_cond_destroy(pthread_cond_t *cond)


int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex)用于等待直到条件被满足。


int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict timeout)同上,并且有等待时间限制,超时会返回ETIMEDOUT错误并终止等待。


timespec的结构定义如下:


struct timespec {

      time_t tv_sec;       /* seconds */

      long   tv_nsec;      /* nanoseconds */

};


   这里输入的时间是绝对时间,而不是相对时间,应先获取系统时间,再加上偏移量。


   int pthread_cond_signal(pthread_cond_t *cond)

int pthread_cond_broadcast(pthread_cond_t *cond)


以上两个函数用于通知线程条件已满足,第一个是通知单个线程,而第二个是广播给多个线程。


BY:HW-HH

点赞
收藏
1
回复
2
分享
举报
浏览2547 编辑于2019-10-10 09:07未知归属地
全部评论
最多点赞
最新发布
最早发布
我发线 api9中没有 pthread_setcanceltype 函数
2楼回复于2023-12-28 14:22 未知归属地

有点没看懂,我要多多学习了

1楼回复于2019-10-10 09:46 未知归属地
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

了解社区公约,与您携手共创和谐专业的开发者社区。