背景简介
在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






































苏公安网备 32011402010933号