一,一次性初始化
pthread_once_t once_control = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *once_control,void(*init_routine)(void));
参数1: 控制变量
参数2: 初始化函数,这个函数由作者完成,内容为你需要初始化的语句。
返回值:
若成功返回0,若失败返回错误编号。
注意:
(1) once_control必须先使用PTHREAD_ONCE_INIT宏静态地初始化然后才能调用pthread_once函数。
(2) pthread_once函数可以在多处调用,或者调用多次,但是只执行一次,
pthread_once函数首先检查控制变量,判断是否已经完成初始化,如果完成就直接return;
否则,pthread_once调用初始化函数。
(3) 某个线程初始化时,另外的线程调用pthread_once,则调用线程等待,直到那个线程完成初始化。
举例如下在线程私有数据举例中有,请参考。
二,线程私有数据
1,key的创建与销毁
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
参数1:为指向一个键值的指针,
参数2:为一个如同C++中的析构函数,当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。
int pthread_key_delete(pthread_key_t key);
2,key与私有数据指针的绑定
int pthread_setspecific(pthread_key_t key,const void *pointer));//将指针与key绑定
void* pthread_getspecific(pthread_key_t key);//通过key获得对应地址。
注意点:
(1)虽然是同一个key,但是在不同的线程里不会冲突,设置同样的key,得到的指针是分别与各自线程对应的指针。
(2)作用在于每个线程可以定义各自的私有数据,而且只能通过key得到地址,每个线程只能通过key得到自己的指针,得不到其他线程的指针,这样就会互不干扰,各自私藏。
举例如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_LEN (100)
pthread_key_t key;
pthread_once_t once_init = PTHREAD_ONCE_INIT;
void release_buffer(void* ptr)
{
printf("Thread %lx release buffer addr at %ld\n",pthread_self(),(long)ptr);
free(ptr);
}
void create_key()
{
pthread_key_create(&key,release_buffer);
printf("Thread %lx create key\n",pthread_self());
}
void gen_data(int* num_ptr)
{
int *ptr = NULL;
pthread_once(&once_init,create_key);//一次性初始化,只会执行一次。
ptr=(int*)pthread_getspecific(key);//获得key绑定的指针地址
if(ptr == NULL)
{
ptr=(int*)malloc(NUM_LEN*sizeof(int));
pthread_setspecific(key,(void*)ptr);
printf("Thread %lx: allocating buffer addr at %ld\n",pthread_self(),(long)ptr);
ptr[0] = num_ptr[0];
}
else
printf("Thread %lx: allocating buffer addr at %ld buffer[0] = %d \n",pthread_self(),(long)ptr,ptr[0]);
}
void* thread_fun(void* num_ptr)
{
int i = 0;
for(i = 0; i < 2; i++)//2次是为了证明buffer[0]不是覆盖碰巧对的,第一次赋值,第二次打印
{
gen_data((int*)num_ptr);
sched_yield();//将处理器交给另一个等待的程序
}
}
int main(int argc,char* argv[])
{
pthread_t th1,th2;
int num[3] = {0,1,2};
thread_fun((void*)(&num[0]));
pthread_create(&th1,NULL,thread_fun,(void*)&num[1]);
pthread_create(&th2,NULL,thread_fun,(void*)&num[2]);
pthread_join(th1,NULL);
pthread_join(th2,NULL);
pthread_exit(0);
return 0;
}
三,线程取消
1,函数介绍
int pthread_cancel(pthread_t thread)
作用:发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止,只是提出请求.
直到到达某个取消点(CancellationPoint)。取消点是线程检查是否被取消并按照请求进行动作的一个位置.
int pthread_setcancelstate(int state, int *oldstate)
作用: 设置取消状态使能或者不使能。
state:有两种值PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE,
分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行。
old_state:NULL或者存入原来的Cancel状态以便恢复。
int pthread_setcanceltype(int type, int *oldtype)
作用:设置本线程取消动作的执行时机
type:有两种取值PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS,
仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和立即执行取消动作(退出);
oldtype如果不为NULL则存入运来的取消动作类型值。
void pthread_testcancel(void)
作用:在不包含取消点,又需要取消点的地方创建一个取消点,线程取消功能处于启用状态且取消状态设置为推迟取消状态时,
pthread_testcancel()函数有效。
2,三种取消状态
Off 禁用取消
Deferred 推迟取消:在下一个取消点执行取消//系统默认
Asynchronous 异步取消:可以随时执行取消
(1)推迟取消:在下一个取消点执行取消
线程调用pthread_cancel函数后,被取消线程不会立即取消,仅仅在到达取消点时响应取消请求。
而取消点在如pthread_testcancel,sleep,pthread_cond_wait等处。
#include<stdio.h>
#include<pthread.h>
int cnt = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *thread_fun(void *arg)
{
//pthread_mutex_lock(&mutex);
while(1)
{
if(++cnt%20==0)
pthread_testcancel(); //此处如果上琐,main函数上琐就会出现死琐,因为这里还未解锁线程就return啦
}
//pthread_mutex_unlock(&mutex);
}
int main()
{
pthread_t th;
pthread_create(&th,NULL,thread_fun,NULL);
sleep(1);
pthread_cancel(th);
void *result;
pthread_join(th,&result);
if(result==PTHREAD_CANCELED)
printf("cancel point = %d\n",cnt);
else
printf("cancel fail\n");
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
return 0;
}
(2)禁用取消
暂时在代码的那个区域停用取消
#include<stdio.h>
#include<pthread.h>
int cnt = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *thread_fun(void *arg)
{
int state;
while(1)
{
if(++cnt < 100)
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,&state);
if(cnt%200 == 0)
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,&state);
pthread_testcancel();
}
}
int main()
{
pthread_t th;
pthread_create(&th,NULL,thread_fun,NULL);
pthread_cancel(th);
void *result;
pthread_join(th,&result);
if(result==PTHREAD_CANCELED)
printf("cancel point = %d\n",cnt);
else
printf("cancel fail\n");
return 0;
}
(3).异步取消:可以随时执行取消,很危险的行为。
下面的例子你会发现你执行几次,每次cancel point的值都不一样,因为取消点不存在,不可控。
#include<stdio.h>
#include<pthread.h>
int cnt = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *thread_fun(void *arg)
{
int cancel_type;
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&cancel_type);
while(1)cnt++;
}
int main()
{
pthread_t th;
pthread_create(&th,NULL,thread_fun,NULL);
sleep(1);
pthread_cancel(th);
void *result;
pthread_join(th,&result);
if(result==PTHREAD_CANCELED)
printf("cancel point = %d\n",cnt);
else
printf("cancel fail\n");
return 0;
}
3,收尾清除工作
void pthread_cleanup_push(void (*routine) (void *), void *arg)//将清除函数加到栈中
void pthread_cleanup_pop(int execute)调用清除函数
注意点:
(1)采用先入后出的栈结构管理
(2)当所有活动的清除处理函数返回时,线程被终止。
(3)execute为0表示不执行,非0为执行;但不影响异常终止时清理函数的执行
举例前面所说的会出现死锁的问题就是资源锁为释放,这里有个清除函数就可以解决死锁问题。
#include<stdio.h>
#include<pthread.h>
int cnt = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void cleanup_fun(void *arg)
{
printf("clean up\n");
pthread_mutex_unlock(&mutex);
}
void *thread_fun(void *arg)
{
pthread_cleanup_push(cleanup_fun,NULL);
pthread_mutex_lock(&mutex);
while(0)
{
if(++cnt%20==0)
pthread_testcancel(); //此处如果上琐,main函数上琐就会出现死琐,因为这里还未解锁线程就return啦,加入清除函数就OK
//pthread_cleanup_pop(0);
}
pthread_mutex_unlock(&mutex);
pthread_cleanup_pop(0);//0正常执行退出是不执行清除函数的,1无论何时都执行
return NULL;
}
int main()
{
pthread_t th;
pthread_create(&th,NULL,thread_fun,NULL);
sleep(1);
pthread_cancel(th);
void *result;
pthread_join(th,&result);
if(result==PTHREAD_CANCELED)
printf("cancel point = %d\n",cnt);
else
printf("cancel fail\n");
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
return 0;
}
四,实时调度
1, 三种调度策略:
SCHED_OTHER: 分时调度策略,系统创建线程时,默认的线程是SCHED_OTHER,所有的线程的优先级别都是0。系统使用这种调度策略,程序将无法设置线程的优先级。
SCHED_FIFO: 实时调度策略,先到先服务。一旦占用cpu则一直运行,或是自愿放弃。
支持优先级的使用一直运行直 到 有更高优先级任务到达或自己放弃。
SCHED_RR: 实时调度策略,时间片轮转。支持优先级的使用当进程的时间片用完,系统将重新分配时间片,
并置于就绪队列尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平。
2,获取具体调度策略的静态优先级范围,各个调度策略支持的最大和最小的优先级可以通过下面的两个函数获得
int sched_get_priority_max(int policy);
int sched_get_priority_min(int policy);
举例如下:
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
void print_priority_for_policy(int policy)
{
printf("max_priority=%d\n",sched_get_priority_max(policy));
printf("min_priority=%d\n",sched_get_priority_min(policy));
}
int main(void)
{
//不同策略的优先级
print_priority_for_policy(SCHED_FIFO);
print_priority_for_policy(SCHED_RR);
print_priority_for_policy(SCHED_OTHER);
}
3,调度策略设置以及获取
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(pthread_attr_t *attr, int *policy);
4,用来设置和获得线程属性attr的调度参数param即设置或者获取当前线程的优先级
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
struct sched_param
{
int __sched_priority; //所要设定的线程优先级
};
举例如下:
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
void print_policy(pthread_attr_t* attr_ptr)
{
int policy;
pthread_attr_getschedpolicy(attr_ptr,&policy);
if(SCHED_FIFO == policy)
printf("policy = SCHED_FIFO\n");
else if(SCHED_RR == policy)
printf("policy = SCHED_RR\n");
else if(policy == SCHED_OTHER)
printf("policy = SCHED_OTHER\n");
else
printf("policy = error\n");
}
void print_priority_for_policy(int policy)
{
printf("max_priority=%d\n",sched_get_priority_max(policy));
printf("min_priority=%d\n",sched_get_priority_min(policy));
}
void print_priority_for_param(pthread_attr_t* attr_ptr)
{
struct sched_param param;
pthread_attr_getschedparam(attr_ptr,¶m);
printf("priority=%d\n",param.__sched_priority);
}
int main(void)
{
//1,不同策略的优先级范围
print_priority_for_policy(SCHED_FIFO);
print_priority_for_policy(SCHED_RR);
print_priority_for_policy(SCHED_OTHER);
//2,初始化属性
pthread_attr_t attr;
pthread_attr_init(&attr);
//3,该线程属性的默认策略值以及优先级
print_policy(&attr);
print_priority_for_param(&attr);
//4,修改策略后的默认优先级以及修改后的优先级
pthread_attr_setschedpolicy(&attr,SCHED_RR);
print_policy(&attr);
print_priority_for_param(&attr);
struct sched_param param;
param.__sched_priority = 98;
pthread_attr_setschedparam(&attr, ¶m);
print_priority_for_param(&attr);
//5,继续修改策略以及该线程的默认优先级
pthread_attr_setschedpolicy(&attr,SCHED_FIFO);
print_policy(&attr);
print_priority_for_param(&attr);
//6,销毁对于属性
pthread_attr_destroy(&attr);
return 0;
}
五,线程和核实体