线程中止方式:
- exit终止进程,无法统计结果
- 线程函数返回return,主动终止当前线程
- 线程执行pthread_exit()函数,主动终止当前线程
- 主线程执行pthread_kill()函数,向其他线程发送结束信号,被动结束
- 主线程执行pthread_cancel()函数,向其他线程发送取消信号,被动结束
1.主动退出:
线程通过函数返回或pthread_exit()函数主动结束当前进程,线程执行完函数逻辑后主动调用.缺点是线程在进程意外结束或接收到kill相关signal时无法终止,尤其对于存在阻塞操作的线程(socket读写)
#include <pthread.h>
void pthread_exit(void *ptr);
其他线程可以通过ptr参数访问线程退出时的返回值
示例:
#include <pthread.h>
void* func1(void*arg)
{
int ret = 0;
printf("function1 is running");
pthread_exit((void*)ret);
}
int main()
{
pthread_t pid1;
int error;
error = pthread_create(pid1,NULL,func1,NULL);
if(error != 0)
{
err_exit(error,"creat thread failed");
}
error = pthread_join(pid1,NULL);
if(error != 0)
{
err_exit(error,"join thread failed");
}
func();
...
return 0;
}
2.被动结束
2.1 pthread_cancel
pthread_cancel函数用于线程向同一进程的其他线程发送中止请求,相当于调用PTHRAED_CANCELED参数的pthread_exit函数,使线程从取消点退出。该函数成功仅代表发送中止请求成功,不代表线程已安全退出。对于接收到的CANCEL信号,如何处理由该线程决定。调用pthread_cancel函数发送取消线程请求后,默认调用pthread_join函数阻塞等待释放线程资源。
子线程接收到cancel信号后,会在系统调用中设置一个cancelpoint
,一般只在系统调用时启动退出线程操作。如sleep()函数,printf或者pthread_testcancel函数。当子线程函数全部为逻辑运算函数时不会经过取消点,线程不能正常退出。
#include <pthread.h>
int pthread_cancel(pthread_t pid);
示例:
#include <pthread.h>
void* func1(void*arg)
{
sleep(1);
printf("function1 is running");
}
void* func2(void*arg)
{
int a = 0;
for(;;)
{
a++;
}
}
int main()
{
pthread_t pid1,pid2;
int error;
error = pthread_create(&pid1,NULL,func1,NULL);
if(error != 0)
{
err_exit(error,"creat thread1 failed");
}
error = pthread_create(&pid2,NULL,func2,NULL);
if(error != 0)
{
err_exit(error,"creat thread2 failed");
}
pthread_cancle(pid1);
error = pthread_join(pid1,NULL);
if(error != 0)
{
err_exit(error,"join thread1 failed");
}
pthread_cancle(pid2);
error = pthread_join(pid2,NULL);
if(error != 0)
{
err_exit(error,"join thread2 failed");
}
return 0;
}
在示例中线程1可以正常结束,线程2未经过取消点,无法正常退出
在执行pthread_cancel函数前能够设置取消点及取消时机
- 设置本线程对Cancel信号的反应
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)
检查本线程是否处于Canceld状态,如果是,则进行取消动作,否则直接返回。 此函数在线程内执行,执行的位置就是线程退出的位置,在执行此函数以前,线程内部的相关资源申请一定要释放掉,否则容易造成内存泄露。(在执行大量逻辑运算时,可以调用 pthread_testcancel()作为取消点 )
2.2 pthread_kill
向子线程发送信号,线程收到信号后处理(事实上不是结束进程,而是向进程传递一个signal)
#include <pthread.h>
#include <signal.h>
int pthread_kill(pthread_t pid, int signal);
参数signal为0时探测线程存活状态。执行成功返回0,否则返回错误码,ESRCH线程不存在,EINVAL信号不合法。
所传入信号
如果需要对传入信号有对应行为,需要在线程内部实现signal函数
示例:
#include <pthread.h>
#include <signal.h>
void* func1(void*arg)
{
printf("function1 is running");
return ((void*)0);
}
int main()
{
pthread_t pid1;
int error;
error = pthread_create(pid1,NULL,func1,NULL);
if(error != 0)
{
err_exit(error,"creat thread1 failed");
}
int kill_rc = pthread_kill(&pid1,0);
if(kill_rc == ESRCH) printf("the specified thread did not exists or already quit\n");
else if(kill_rc == EINVAL) printf("signal is invalid\n");
else printf("the specified thread is alive\n");
return 0;
}
3.线程处理程序
为了防止一些线程非正常退出时,相关资源没有来得及释放,后续请求时发生死锁,需要在退出时释放资源(如锁资源)
#include <pthread.h>
void cleanup(void *arg);//线程清理函数
void pthread_cleanup_push(void (*rtn)(*arg),void* arg);
void pthread_cleanup_pop(void (int execute);
当pthread_cleanup_pop()函数的参数为0时,仅仅在线程调用pthread_exit函数或者其它线程对本线程调用 pthread_cancel函数时,才在弹出“清理函数”的同时执行该“清理函数”。清理程序是按照与它们安装时相反的顺序被调用的。
push进去的函数可能在以下三个时机执行:
1 显示的调用pthread_exit();
2 在cancel点线程被cancel。
3 pthread_cleanup_pop()的参数不为0时。
示例:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void cleaner(void *arg)
{
printf("cleanup:%s\n",(char*)arg);
}
void *thread1(void *arg)
{
printf("thread 1 begin\n");
pthread_cleanup_push(cleaner,"thread 1 11 handler");
pthread_cleanup_push(cleaner,"thread 1 22 handler");
printf("thread 1 push ok \n");
if(arg)
return ((void *)1);
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
return ((void *)3);
}
void *thread2(void *arg)
{
printf("thread 2 begin\n");
pthread_cleanup_push(cleaner,"thread 2 11 handler");
pthread_cleanup_push(cleaner,"thread 2 22 handler");
printf("thread 2 push ok \n");
if(arg)
pthread_exit((void *)2);
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
pthread_exit((void *)4);
}
int main()
{
int err;
pthread_t t1,t2;
void *tret;
err = pthread_create(&t1,NULL,thread1,(void *)1);//修改点
if(err != 0)
{
fprintf(stderr,"thread create 1 is error\n");
return -1;
}
err = pthread_create(&t2,NULL,thread2,(void *)1);//修改点
if(err != 0)
{
fprintf(stderr,"thread create 2 is error\n");
return -2;
}
err = pthread_join(t1,&tret);
if(err != 0)
{
fprintf(stderr,"can't join with thread 1\n");
return -2;
}
//pthread_cancel(t1);
printf("thread 1 exit code %d\n",tret);
err = pthread_join(t2,&tret);
if(err != 0)
{
fprintf(stderr,"can't join with thread 2\n");
return -2;
}
printf("thread 2 exit code %d\n",tret);
return 0;
}