一. 线程清理和控制函数
如同进程可以调用atexit函数安排在它退出时需要调用的函数一样,线程也可以安排在它退出时执行一些函数。这些清理函数记录在栈中,所以它们执行的顺序和注册的顺序是相反的。
#include <pthread.h>
void pthread_cleanup_push(void(*rtn)(void*), void *arg);
void pthread_cleanup_pop(int execute);
返回:成功返回0,否则返回错误编号;
参数:rtn:清理函数指针;
arg:调用清理函数传递的参数;
execute:值1时执行线程清理函数,值0时不执行线程清理函数;
触发线程调用清理函数的动作:
1)调用pthread_exit;
2)响应取消请求;
3)用非零execute参数调用thread_cleanup_pop时;
注意:pthread_cleanup_push和pthread_cleanup_pop时成对儿出现的。
下面给出一个案例。
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
// 定义线程清理函数
void clean_fun(void *arg)
{
char *s = (char*)arg;
printf("clean_func: %s\n", s);
}
void *th_fn(void *arg)
{
int execute = int(arg);
pthread_cleanup_push(clean_fun , "first clean func");
pthread_cleanup_push(clean_fun, "sencond clean func");
printf("thread running %lx\n", pthread_self());
pthread_cleanup_pop(execute);
pthread_cleanup_pop(execute);
return (void*)0;
}
int main(void)
{
int err;
pthread_t th1, th2;
if((err = pthread_create(&th1, NULL, th_fn, (void*)1)) != 0)
perror("pthread create error");
pthread_join(th1, NULL);
printf("th1(%lx) finished\n", th1);
if((err = pthread_create(&th2, NULL, th_fn, (void*)1)) != 0)
perror("pthread create error");
printf("th2(%lx) finished\n", th2);
return 0;
}
上面程序的运行结果如下:
关于线程清理函数的一些细节,请看这篇文章。点击这里
二. 线程属性的初始化、销毁、设置和获得分离属性
1. 线程属性的初始化和销毁
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
返回:成功返回0,否则返回错误编号;
线程属性结构(pthread_attr_t)
2. 设置和获得分离属性
#include <pthread.h>
int pthread_attr_getdetachstat(const pthread_attr_t *restrict attr, int *detachstate);
int pthread_attr_setdetachstat(const pthread_attr_t *attr, int detachstate);
返回:成功返回0,出错返回错误编号;
说明:1)detachstate的取值
PTHREAD_CREATE_JOINABLE(默认值)---------正常启动线程
PTHREAD_CREATE_DETACHED------------------以分离状态启动线程
2)以默认方式启动的线程,在线程结束后不会自动释放占有的系统资源,要在主控线程中调用pthread_join()后才会释放;
3)以分离状态启动的线程,在线程结束后会自动释放所有占用的系统资源(主线程不需用调用pthread_join函数)
下面给出一个具体的案例。
#include <pthread.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
void out_state(pthread_attr_t *attr)
{
int state;
// 获取分离属性值
if(pthread_attr_getdetachstate(attr, &state) != 0)
perror("getdetachstate error");
else
{
if(state == PTHREAD_CREATE_JOINABLE)
printf("joinable state\n");
else if(state == PTHREAD_CREATE_DETACHED)
printf("detached state\n");
else
printf("error state\n");
}
}
void *th_fn(void *arg)
{
int i;
int sum = 0;
for(i = 1; i <= 100; ++i)
sum += i;
return (void*)∑
}
int main(void)
{
int err;
pthread_t default_th, detach_th;
// 定义线程属性
pthread_attr_t attr;
// 对线程属性初始化
pthread_attr_init(&attr);
// 输出分离属性(默认值)
out_state(&attr);
// 取分离属性的默认值,以正常方式启动子线程
if((err = pthread_create(&default_th, &attr,
th_fn, (void*)0)) != 0)
{
perror("pthread create error");
}
int res;
if((err = pthread_join(default_th,
(void*)&res)) != 0)
{
perror("pthread join error");
}
else
{
printf("default return is %d\n", (int)res);
}
printf("-------------------------------\n");
//设置分离属性为分离状态属性
pthread_attr_setdetachstate(&attr,
PTHREAD_CREATE_DETACHED);
out_state(&attr);
//以分离状态启动子线程
if((err = pthread_create(&detach_th, &attr,
th_fn, (void*)0)) != 0)
{
perror("pthread create error");
}
//以分离状态启动的子线程,这里就不要调用pthread_join了
/*
if((err = pthread_join(detach_th, (void*)&res)) != 0)
{
// perror("pthread_join error");
fprintf(stderr, "%s\n", strerror(err));
}
else
{
printf("detach rerurn is %d\n", (int)res);
}
*/
// 销毁线程属性
pthread_attr_destroy(&attr);
printf("0x%lx finished\n", pthread_self());
sleep(1);
return 0;
}
上面的程序运行结果如下:
在该案例中,第一个线程的属性我们以默认的方式初始化,因此主线需要调用pthread_join进行等待;第二个线程我们设置线程的属性为分离状态,在该线程结束后会自动释放所占用的系统资源,因此主线程就不再需要调用pthread_join进行等待。
注:所谓的分离,通俗一点理解就是:创建子线程后,子线程与主控线程已经没有关系,子线程执行完毕后会自动去释放资源,而不需要主线程去调用pthread_join进行回收。
所以,分离属性一般在网络通讯中应用较多(服务器端),因为对于高并发的多线程访问需要使用完后自动释放资源,否则会造成网络的拥堵。