POSIX标准意在期望获得源代码级别的软件可移植性。对于一个使用POSIX标准的程序,可以在所有支持POSIX标准的系统上运行.
POSIX标准为多线程提供了一组统一的接口.它可以非常好的在linux以及各种unix与windows上编译运行
C++11的多线程库,也是封装了POSIX多线程接口.可见POSIX标准应该范围很广.
//使用POSIX多线程接口需要加载头文件
#include <pthread.h>
//一.线程的创建
int pthread_create(pthread_t *threadid,
pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg);
/*
参数:
threadid: 参数是一个指针,当线程成功创建时,返回新线程ID。
attr: 用于指定线程的属性
start_routine: 该参数是一个函数指针,指向线程创建后要调用的函数。
arg: 传递给线程函数的参数。
返回值:
成功: 0
失败: 错误编号
*/
//得到线程id
pthread_t pthread_self(void);
//pthread_self用来得到当前线程的id.可以使用一个prhread_t类型的结构接收这个id.
//线程id比较
int pthread_equal(pthread_t thread1,pthread_t thread2);
//pthread_equal函数用来比较两个线程id是否相同.
//线程变量一次性初始化函数,这个函数看起来非常有用.
int pthread_once(pthread_once_t *once_control,void(*init_routine)(void));
/*在程序设计中,单例模式是常常遇到的.这个函数保证了在所有线程中,用来初始化单例对象的函数,只被执行一次.
参数:
once_control 控制变量
init_routine 初始化函数
返回值:
若成功返回0,若失败返回错误编号。
once_control 是一个控制变量.用来控制初始化函数是否被运行过.
它必须使用PTHREAD_ONCE_INIT宏来进行初始化.如下:
pthread_once_t once_control = PTHREAD_ONCE_INIT;
*/
//下面是简单用例.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
//线程函数
int * thread(void *arg)
{
//因为有控制变量的保护,在多线程同时运行时,once_init函数只会被运行一次.
pthread_once(once_control, once_init);
printf("thread id is %d.\n",pthread_self());
return NULL;
}
//单例对象以及它的初始化函数
static unsigned int once;
void once_init()
{
once = 0x9234;
}
//pthread_once的控制变量
pthread_once_t once_control = PTHREAD_ONCE_INIT;
int main()
{
pthread_t id;
//打印主函数所在线程ID
printf("Main thread id is %d \n",pthread_self());
if(pthread_create(&id,NULL,(void *)thread,NULL)) {
printf("Fail to Create Thread");
return -1;
}
//因为有控制变量的保护,在多线程同时运行时,once_init函数只会被运行一次.
pthread_once(once_control, once_init);
return 0
}
//二.线程属性
typedef struct
{
int detachstate; //是否与其他线程脱离同步
int schedpolicy; //新线程的调度策略
struct sched_param schedparam; //运行优先级等
int inheritsched; //是否继承调用者线程的值
int scope; //线程竞争CPU的范围(优先级的范围)
size_t guardsize; //警戒堆栈的大小
int stackaddr_set; //保留位
void * stackaddr; //堆栈地址
size_t stacksize; //堆栈大小
} pthread_attr_t;
/*
属性值不能直接设置,须使用相关函数进行操作,
初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。
成员:
(一)detachstate
表示新线程是否与创建它的进程分离,
缺省为PTHREAD_CREATE_JOINABLE同步状态,即非分离状态。当线程结束时,必须由其它线程调用
pthread_join(pthread_t *threadid);
函数来释放这个线程所占资源.
如果线程没有结束时,其它线程运行pthread_join函数,那么这个运行函数的线程会阻塞到参数线程结束,进行回收资源.
pthread_join函数不能等待一个已经被释放的线程.这会出现段错误而导致程序的崩溃.
如果设置为PTHREAD_CREATE_DETACHED状态,即分离状态.当线程结束时,将由系统回收这个线程的资源.
线程被设置成分离状态之后,将不能再设置回PTHREAD_CREATE_JOINABLE同步状态.
设置为PTHREAD_CREATE_DETACHED状态有两种方式.一种是在线程创建之前.
pthread_attr_t attr; //创建线程属性对象
pthread_attr_init(&attr); //初始化线程属性对象
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //设置线程属性对象为分离
一种是在创建线程之后.使用
int pthread_detach(pthread_t th);
函数来设置指定线程分离
一个好玩的用法
pthread_detach(pthread_self());
(二)schedpolicy 与 schedparam
线程的调度策略与优先级,主要包括:
1.SCHED_OTHER(正常、非实时)
2.SCHED_RR(实时、轮转法)
3.SCHED_FIFO(实时、先入先出)
缺省为SCHED_OTHER,后两种调度策略仅对超级用户有效。
可以通过函数
int pthread_setschedparam(pthread_t target_thread,
int policy,
const struct sched_param *param)
来改变策略与优先级
参数:
1. target_thread是使用pthread_create所获得的线程ID。
2.Policy用于指明使用哪种策略。
SCHED_OTHER
它是默认的线程分时调度策略,所有的线程的优先级别都是0,线程的调度是通过分时来完成的。简单地说,如果系统使用这种调度策略,程序将无法设置线程的优先级。请注意,这种调度策略也是抢占式的,当高优先级的线程准备运行的时候,当前线程将被抢占并进入等待队列。这种调度策略仅仅决定线程在可运行线程队列中的具有相同优先级的线程的运行次序。
SCHED_FIFO
它是一种实时的先进先出调用策略,且只能在超级用户下运行。这种调用策略仅仅被使用于优先级大于0的线程。它意味着,使用SCHED_FIFO的可运行线程将一直抢占使用SCHED_OTHER的运行线程J。此外SCHED_FIFO是一个非分时的简单调度策略,当一个线程变成可运行状态,它将被追加到对应优先级队列的尾部((POSIX1003.1)。当所有高优先级的线程终止或者阻塞时,它将被运行。对于相同优先级别的线程,按照简单的先进先运行的规则运行。我们考虑一种很坏的情况,如果有若干相同优先级的线程等待执行,然而最早执行的线程无终止或者阻塞动作,那么其他线程是无法执行的,除非当前线程调用如pthread_yield之类的函数,所以在使用SCHED_FIFO的时候要小心处理相同级别线程的动作。
SCHED_RR
鉴于SCHED_FIFO调度策略的一些缺点,SCHED_RR对SCHED_FIFO做出了一些增强功能。从实质上看,它还是SCHED_FIFO调用策略。它使用最大运行时间来限制当前进程的运行,当运行时间大于等于最大运行时间的时候,当前线程将被切换并放置于相同优先级队列的最后。这样做的好处是其他具有相同级别的线程能在“自私“线程下执行。
3.param是struct sched_param类型的指针,sched_param类型仅仅包含一个成员变sched_priority,指明所要设置的静态线程优先级
默认值是0;
(三)inheritsched
线程继承的调度策略,有两种值可供选择:
PTHREAD_EXPLICIT_SCHED 新线程使用显式指定调度策略和调度参数(即attr中的值)
PTHREAD_INHERIT_SCHED,继承调用者线程的值。
缺省为PTHREAD_EXPLICIT_SCHED。
可以使用下面两个函数获得属性与设置
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched);
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
(四)scope
线程间竞争CPU的范围
POSIX的标准中定义了两个值:
PTHREAD_SCOPE_SYSTEM 系统中所有线程一起竞争CPU时间
PTHREAD_SCOPE_PROCESS,与同进程中的线程竞争CPU。
目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。
可以使用下面两个函数获得属性与设置
int pthread_attr_getscope(const pthread_attr_t *attr,int *contentionscope);
int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);
(五)guardsize
警戒堆栈的大小
线程栈末尾之后以避免栈溢出的扩展内存大小。这个属性默认设置为PAGESIZE个字节。可以把guardsize线程属性设为0,从而不允许属性的这种特征行为发生:在这种情况下不会提供警戒缓存区。同样地,如果对线程属性stackaddr作了修改,系统就会假设我们会自己管理栈,并使警戒栈缓冲区机制无效,等同于把guardsize线程属性设为0。
(六)stackaddr_set //reserved
保留未定义.
(七)stackaddr;
线程堆栈地址设置
可以使用下面两个函数获得属性与设置
int pthread_attr_getstackaddr(const pthread_attr_t *, void **);
int pthread_attr_setstackaddr(pthread_attr_t *, void *);
(八)stacksize; //堆栈大小
可以使用下面两个函数获得属性与设置
int pthread_attr_getstacksize(const pthread_attr_t *, size_t *);
int pthread_attr_setstacksize(pthread_attr_t *, size_t);