pthread线程库使用介绍

        多线程的开发和应用在平时的项目中使用非常频繁。Linux C++中,一般使用pthread库操作线程相关业务,不少公司一般都会基于该线程库的基本接口进行封装,使用起来更加方便易用。本文对该线程库的常用接口和使用方法做个介绍。

一、创建线程前的工作——设置线程属性

1、线程属性结构体的成分

        线程具有属性,属性结构体用pthread_attr_t表示。它的结构如下:

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;

2、属性初始化接口

        提供的接口:

int pthread_attr_init(pthread_attr* attr);

        使用时,首先要先对这个结构体进行初始化,在使用后需要对其反初始化。

3、属性反初始化接口

        提供的接口:

int pthread_attr_destroy(pthread_attr_t* attr);

        同上一点的初始化,使用结束后的销毁。

4、设置、获取线程作用域属性scope接口

        提供的接口:

int pthread_attr_setscope(pthread_attr_t*, int);

int pthread_attr_getscope(const pthread_attr_t*, int*);

        作用域属性表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。具体可以选择两个值,PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS。前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU,默认的一般都是PTHREAD_SCOPE_PROCESS。

5、设置、获取线程分离状态属性detachstate接口

        提供的接口:

int pthread_attr_setdetachstate(pthread_attr_t*, int);

int pthread_attr_getdetachstate(const pthread_attr_t*, int*);

       在任何一个时间点上,线程是可结合的(joinable),或者是可分离的(detached),一个可结合的线程能够被其他线程收回其资源和杀死,在被其他线程回收之前,它的存储器资源(如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。默认情况下,线程是非分离状态的。这种情况下,原有的线程等待创建的线程结束,只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。这两种状态的值分别是PTHREAD_CREATE_DETACHED(可分离线程)和PTHREAD_CREATE_JOINABLE(非分离线程),默认是PTHREAD_CREATE_JOINABLE。

6、设置、获取线程调度参数接口

         提供的接口:

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

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

        接口中的struct sched_param结构体仅仅包含一个成员变量sched_priority,大的优先权值对应高的优先权,系统支持的最大和最小优先权值可以用sched_get_priority_max和sched_get_priority_min分别得到。但是在一般情况下,若不是写实时程序,不建议修改线程的优先级,因为调度策略是一个复杂的事情,如果不正确使用会导致程序错误,从而导致死锁问题,普通线程一般不用设置。

7、设置、获取线程的调度策略接口

        提供的接口:

int pthread_attr_setschedpolicy(pthread_attr_t*, int);

int pthread_attr_getschedpolicy(const pthread_attr_t*, int*);

        Linux线程调度策略及优先级说明:

        Linux系统下任务调度策略一般有三种:

        SCHED_OTHER:普通任务调度策略。

        SCHED_FIFO:实时任务调度策略,先到先服务。一旦占用CPU则一直运行,知道有更高优先级任务到达或自己放弃。

        SCHED_RR:实时任务调度策略,时间片轮转。当任务的时间片用完,系统将重新分配时间片,并置于就绪队列尾。普通线程一般设置SCHED_OTHER。

8、设置、获取线程的继承调度

        提供的接口:

int pthread_attr_setinheritsched(pthread_attr_t*. int);

int pthread_attr_getinheritsched(const pthread_attr_t*. int*);

        线程的继承调度主要是设置线程是否继承父线程的调度,也就是说此线程的调度策略及优先级是否继承于父线程。可以选择两个值:PTHREAD_INHERIT_SCHED和PTHREAD_EXPLICIT_SCHED,默认情况下,普通线程是继承于父线程的。设置好属性之后,接下来就是线程的创建了。

二、创建线程

        创建线程接口:

int pthread_create(pthread_t *thread, const pthread_attr_t* attr, void* (*start_routine)(void *), void* arg);

        参数说明:

        参数一为指向线程标识符的指针,也就是线程句柄。

        参数二为设置的线程属性,就是上面第一节中通过接口设置的属性结构体。

        参数三为线程运行函数的地址,也就是线程执行体的函数指针。

        参数四为线程运行函数的参数。

        使用该接口创建线程时,若不指定分配堆栈大小,系统会分配默认值。在Linux中,使用ulimit -s可以查看默认值的大小。Linux默认分配8192KB也就是8M内存。一般来说,默认堆栈是8192KB,最小为16KB。如果在创建线程时,出现内存不足问题,则pthread_create会返回错误码12。

        线程创建成功后,设置pthread_detach可以将线程状态更改为detach(分离)状态,正如上面所述,Linux中的线程有两种状态,joinable和detached,分别对应线程结束或退出时,线程创建的栈是否被析构,若为joinable,则不会自动析构,需要手动调用pthread_join()接口进行析构,若是detached,则可以确保资源的释放。线程创建后,调用这个借口将线程设置为detached状态,可以确保资源最终被释放。

int pthread_detach(pthread_t thread);

三、线程阻塞接口

        接口定义:

int pthread_join(pthread_t thread, void** retval);

        这个接口是将子线程合入主线程,主线程阻塞等待子线程结束,然后回收子线程资源。

四、获取线程ID方法

        线程ID的获取可以用gettid方法:

pid_t tid;

tid = syscall(SYS_gettid);

        或者使用pthread_self()接口。但gettid和pthread_self()是有区别的。gettid获取的是内核中的线程ID,pthread_self获取的是POSIX thread ID。但gettid不可移植,只能用在LInux系统下,pthread_self()在Linux下与系统thread是一对一模型,可以认为一个gettid必然对应一个pthread_id,但二者之间不可相互转换。

五、线程休眠

        线程休眠可以使用sleep()(或者usleep()等)或sched_yield(),但是这两个接口也有一些区别。sced_yield()可以使用另一个级别等于或高于当前线程的线程先运行,如果没有符合条件的线程,那么这个函数将会立刻返回然后继续执行当前线程的程序。而sleep则是等待一段时间后再等待CPU的调度,然后去获得CPU资源,在毫秒级别的usleep下,这种情况可能会导致休眠时间不准。

六、线程正常终止的方法

        方法一:return从线程函数返回。

        方法二:通过调用函数pthread_exit使线程退出。

        方法三:线程可以被统一进程中的其他线程取消。

        主线程中,main函数中调用了return或是调用了exit函数,则主线程退出,且整个线程也会终止,此时进程中所有的线程也将终止。

        主线程中调用pthread_exit,则仅仅是主线程结束,进程不会结束。进程内的其它线程也不会结束,直到所有的线程结束,进程才会终止。

        在任何一个线程中调用exit函数都会导致进程结束。进程一旦结束,所有进程中的线程都将结束。

#include <signal.h>

int pthread_kill(pthread_t thread, int sig);

        这个接口作用是向指定ID的线程发送sig信号,如果线程的代码内不作任何信号处理,则会按照信号的默认行为影响整个进程。假如发送了pthread_kill(threadid, SIGKILL),则杀死整个进程。

        以上对Linux下常用的pthread线程库做了基本的介绍,也对一些线程机制做了简单的总结。实际使用时,我们可以根据需要调用各自功能的接口实现对多线程的控制。使用该库编译时要加-lpthread。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值