一,掌握线程的终止方式,线程的连接,退出操作,清理操作。
二,线程的清理操作是如何进行的?
【注】查看标准函数库的定义解释命令,如:man 3 exit
1,线程终止
1)exit是危险的:
若进程中任意一个线程调用了exit(),_Exit(),_exit(),那么整个进程就会终止。
2)不终止进程的退出方式:
3)例子1;验证几种退出方式
建立文件test_several_exits.c;在main函数调用sleep,睡眠2秒确保子线程先运行完,再运行主线程main;内容如下:
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
void *pthread_fun(void *arg)
{
if(strcmp(arg,"1")==0)
{
printf("new return by return\n");
return (void*)1;
}
if(strcmp(arg,"2")==0)
{
printf("new return by pthread_exit\n");
pthread_exit( (void*)2);
}
if(strcmp(arg,"3")==0)
{
printf("new return by exit\n");
exit(3);
}
}
int main(int argc,char *argv[])
{
int errno;
pthread_t tid;
errno=pthread_create(&tid,NULL,pthread_fun,argv[1]);
if(errno!=0)
{
printf("create new thread failure\n");
return errno;
}
printf("create new thread success\n");
sleep(2);//sleep 2 second to wait for the finish of new thread
printf("main thread tid=%ld\n",pthread_self());//run after the new thread ran
return 0;
}
编译之后运行结果如下图:
如下图的缺少参数时会发生段错误;
如下图输入参数为1,即以return方式退出子进程,可见能够输出main函数的内容main thread tid不会运行完子线程就立马终止进程。
如下图输入参数为2,即以pthread_exit方式退出子进程,可见能够输出main函数的内容main thread tid不会运行完子线程就立马终止进程。
如下图输入参数为3,即以exit方式退出子进程,可见没有输出main函数的内容main thread tid,运行完子线程就立马终止进程了。
2,线程的链接
例子2:验证线程的连接;
建立文件thread_join.c,内容如下:
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
void *pthread_fun1(void *arg)
{
printf("this is new thread_1\n");
return (void*)1;
}
void *pthread_fun2(void *arg)
{
printf("this is new thread_2\n");
return (void*)2;
}
int main()
{
int errno_1;
int errno_2;
void *rval_1;
void *rval_2;
pthread_t tid_1;
pthread_t tid_2;
errno_1=pthread_create(&tid_1,NULL,pthread_fun1,NULL);
if(errno_1!=0)
{
printf("create new thread_1 failure\n");
return errno_1;
}
printf("create new thread_1 success\n");
errno_2=pthread_create(&tid_2,NULL,pthread_fun2,NULL);
if(errno_2!=0)
{
printf("create new thread_2 failure\n");
return errno_2;
}
printf("create new thread_2 success\n");
printf("thread_1_jion=%d\n",pthread_join(tid_1,&rval_1));
printf("thread_2_jion=%d\n",pthread_join(tid_1,&rval_2));
printf("thread_1 exit code rval_1=%d\n",(int*)rval_1);
printf("thread_2 exit code rval_2=%d\n",(int*)rval_2);
return 0;
}
运行结果如下:
因为pthread_fun2调用了,pthread_detached 所以其pthread_join返回值为非0的任意数,其返回码rval_2也是随机的码而不是返回码2,即获取不到,因为被分离了(释放了)。
【注意】当线程一旦被连接成功,就无法再次连接,若再连接就会连接失败,对上例2中被连接成功的thread_2发起第二次连接,在main函数最后加入第二次连接代码:
printf("second thread_1_jion=%d\n",pthread_join(tid_1,&rval_1));
printf("second rval_1=%d\n",(int*)rval_1);
运行结果如下:
入结果所示,被成功连接过的thread_1就会自己分离了即pthread_detach了,不能再被第二次连接了。
3,线程的取消
函数解析;
取消函数:int pthread_cancel(pthread_t tid
取消tid指定的线程,只是发送一个信号,不意味等待线程终止,发送成功也不意味着线程一定会终止。
取消状态:
取消类型:
取消点:
例子3:实现正确取消一个线程。
取消流程图:
黑->蓝->橙;思路步骤:主线程创建一个新线程然后睡眠2秒——>新线程调用pthread_setcancelstate()函数将自己的取消状态置为disable(不可取消),然后打印一条语句(说明自己的存在)后睡眠4秒——>程序返回主线程,这时主线程调用pthread_cancel向新线程发送取消信号,然后调用pthread_join函数去等待新线程的结束——>返回新线程,新线程打印一些语句,然后调用pthread_setcancelstate()函数将自己设置为可取消状态enable,这时取消信号已经由主线程发过来,所以新线程被取消,然后我们打印一下被取消的点(结束的代码行)——>返回主线程把pthread_cancel函数的返回值和pthread_join里的新线程的退出码rval打印出来——>结束。【注】通过:man pthreads 查看取消点
建立文件thread_cancel.c;代码如下:
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
void *pthread_fun(void *arg)
{
int cstateval;
//set disable
cstateval=pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
if(cstateval!=0)
{
printf("set cancel state failure\n");
return (void*)cstateval;
}
printf("I'm new thread\n");
sleep(4);
printf("receive main cancel and to set enable\n");
cstateval=pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
if(cstateval !=0)
{
printf("set cancel state to enable failure\n");
return (void*)cstateval;
}
printf("the cancel node is here\n");//output cancel node
printf("this words will never output\n");//next will not output from here
return (void*)2;
}
int main()
{
pthread_t tid;
int errno;
int cval,jval;
int *rval;
errno=pthread_create(&tid,NULL,pthread_fun,NULL);
if(errno!=0)
{
printf("create a new thread failure\n");
return errno;
}
printf("create new thread success\n");
sleep(2);
cval=pthread_cancel(tid);
if(cval!=0)
{
printf("send cancel signal to new thread failure\n");
return cval;
}
jval=pthread_join(tid,&rval);
if(jval!=0)
{
printf(" join new thread failure\n");
return jval;
}
printf("new thread rval=%ld\n",(int*)rval);
return 0;
}
运行结果如下:
如图按思想步骤实现了我们例子的目的。
基于次基础上,将新线程的取消状态设置为enable,即一旦接收到取消信号就立马结束,而不像disable状态,接收到取消信号后需要设置取消状态为enable后线程才会取消。再编译运行;结果如下:
结果证明新线程第一次通过pthread_setcancelstate()函数接收到主线程的信号时就立马被取消了(结束了子进程),并直接返回主线程。
取消类型的实验:基于例子3,加入pthread_setcanceltype(PTHREAD_CANCEL_ANSYNCHRONOUS,NULL);//参数PTHREAD_CANCEL_ANSYNCHRONOUS为立即响应取消请求类型,将不会执行之后的语句;我也就看不到“the cancel node is here"这句话了。
代码修改添加如下:
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
void *pthread_fun(void *arg)
{
int cstateval;
//set disable
cstateval=pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
if(cstateval!=0)
{
printf("set cancel state failure\n");
return (void*)cstateval;
}
printf("I'm new thread\n");
sleep(4);
printf("receive main cancel and to set enable\n");
cstateval=pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
if(cstateval !=0)
{
printf("set cancel state to enable failure\n");
return (void*)cstateval;
}
int typeval;
//ASYNCHRONOUS is iminitely to cancel and we will never see next print
typeval=pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
if(typeval !=0)
{
printf("set cancel type failure\n");
}
printf("the cancel node is here\n");//output cancel node
printf("this words will never output\n");//next will not output from here
return (void*)2;
}
int main()
{
pthread_t tid;
int errno;
int cval,jval;
int *rval;
errno=pthread_create(&tid,NULL,pthread_fun,NULL);
if(errno!=0)
{
printf("create a new thread failure\n");
return errno;
}
printf("create new thread success\n");
sleep(2);
cval=pthread_cancel(tid);
if(cval!=0)
{
printf("send cancel signal to new thread failure\n");
return cval;
}
jval=pthread_join(tid,&rval);
if(jval!=0)
{
printf(" join new thread failure\n");
return jval;
}
printf("new thread rval=%ld\n",(int*)rval);
return 0;
}
运行结果如下图:
向线程发送信号
1,pthread_kill函数,信号处理。
pthread_kill()函数解析:
例子5:实现向线程发送信号
建立文件pthread_kill.c;内容如下:
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include <signal.h>
void *pthread_fun(void *arg)
{
printf("%s\n");
return (void*)1;
}
int main()
{
pthread_t tid;
int errno;
int ret;
void*rval;
errno=pthread_create(&tid,NULL,pthread_fun,"I'm new thread");
if(errno!=0)
{
printf("create new thread failure\n");
return errno;
}
//send signal to new thread
ret=pthread_kill(tid,SIGQUIT);
if(ret == ESRCH)
{
printf("thread tid is not found ESRCH=%d\n",ESRCH);
return ESRCH;
}
pthread_join(tid,&rval);
printf("main thread receive rval =%d\n",(int*)rval);
return 0;
}
运行结果:主线程创建新线程然后睡眠1秒,让新线程运行完,再发送信号给新线程,然后进程退出。
信号的处理:
例子6:演示信号的处理过程;
流程图
程序执行流程:主线程main创建线程1线程2,睡眠1秒(等待新线程启动)——>执行新线程,新线程1调用pthread_sigmask()设置为SIG_BLOCK(即信号阻塞状态,当其他线程发信号过来时将接收不到,就不会执行任何处理函数sig_handler;而新线程2则不调用pthread_sigmask设置信号接收状态,可以正常接收到其他线程发过来的信号,进而可以调用任意处理函数sig_handler)——>新线程2执行完打印输出,睡眠2秒返回主线程——>主线程调用pthread_kill向新线程发送SIGQUIT信号,此时新线程2由于调用了pthread_sigmask设置为SIG_BLOCK屏蔽信号状态所以无法接收该信号,只有没有设置SIG_BLOCK状态的新线程1可以接收该信号,当新线程接收到该信号后调用sig_handler1处理函数去接收该信号并打印出来。
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include <signal.h>
void sig_handler1(int arg)
{
printf("thread1 get signal=%d\n",arg);
return (void)arg;
}
void sig_handler2(int arg)
{
printf("thread2 get signal =%d\n",arg);
return (void)arg;
}
void *pthread_fun1(void* arg)
{
printf("new thread 1\n");
struct sigaction act;
memset(&act,0,sizeof(act));
sigaddset(&act.sa_mask,SIGQUIT);/将信号SIGQUIT加入信号集
act.sa_handler = sig_handler1;
sigaction(SIGQUIT,&act,NULL);
//SIG_BLOCK屏蔽主线程发过来的信号
pthread_sigmask(SIG_BLOCK,&act.sa_mask,NULL);//设置屏蔽掩码
sleep(2);
}
void *pthread_fun2(void* arg)
{
printf("new thread 2\n");
struct sigaction act;
memset(&act,0,sizeof(act));
sigaddset(&act.sa_mask,SIGQUIT);//将信号SIGQUIT加入信号集
act.sa_handler = sig_handler2;
sigaction(SIGQUIT,&act,NULL);
//pthread_sigmask(SIG_BLOCK,&act.sa_mask,NULL);
sleep(2);
}
int main()
{
pthread_t tid1,tid2;
int errno_1;
int errno_2;
int s1,s2;
int *rval_1,*rval_2;
errno_1=pthread_create(&tid1,NULL,pthread_fun1,NULL);
if(errno_1=0)
{
printf("create new thread failurew\n");
return errno_1;
}
errno_2=pthread_create(&tid2,NULL,pthread_fun2,NULL);
if(errno_2=0)
{
printf("create new thread failurew\n");
return errno_2;
}
sleep(1);
s1 = pthread_kill(tid1,SIGQUIT);
if(s1!=0)
{
printf("send signal to thread1 failure\n");
return s1;
}
s2 = pthread_kill(tid2,SIGQUIT);
if(s2!=0)
{
printf("send signal to thread2 failure\n");
return s2;
}
pthread_join(tid1,&rval_1);//等待新线程的运行结束
pthread_join(tid2,&rval_2);
return 0;
}
运行结果如下图:
程序中哪个线程最后调用了sigaction()函数,那么信号就传给哪个处理函数,如例子中要是pthread_fun1最后调用sigaction()的话,就调用它的信号处理函数sig_handler2;同理,反之则调用sig_handler1。【注意】当pthread_sigmask被设置为SIGKILL和SIGSTOP时不会被阻塞,只有设置为SIG_BLOCK时才被阻塞。
线程的清理操作
【注意】pthread_cleanup_push和pthread_cleanup_pop函数所操作的栈是top=0为栈顶指针为初始化的。
例子:线程的清理操作(其实就是进栈和退栈):
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include <signal.h>
void *first_clean(void *arg)
{
printf("%s first clean\n",arg);
return (void*)0;
}
void *second_clean(void *arg)
{
printf("%s second clean\n");
return (void*)1;
}
void *pthread_fun1(void *arg)
{
printf("here is thread1\n");
pthread_cleanup_push(first_clean,"thread1");//第一次将处理程序压入栈
pthread_cleanup_push(first_clean,"thread1");//第二次将处理程序压入栈
pthread_cleanup_pop(2);//第一次出栈清除处理程序
pthread_cleanup_pop(1);//第二次出栈清除处理程序
return (void*)1;
}
void *pthread_fun2(void *arg)
{
printf("here is thread2\n");
pthread_cleanup_push(fsecond_clean,"thread2");//第一次将处理程序
pthread_cleanup_push(second_clean,"thread2");//第二次将处理程序压入栈
pthread_cleanup_pop(1);//第一次出栈清除处理程序
pthread_cleanup_pop(0);//第二次出栈清除处理程序
return (void*)2;
}
int main()
{
pthread_t tid1,tid2;
int errno_1,errno_2;
if(errno_1!=0)
{
printf("create new thread1 failure\n");
return erno_1;
}
printf("create new thread2 failure\n");
if(errno_1!=0)
{
printf("create new thread1 failure\n");
return erno_1;
}
printf("create new thread2 failure\n");
sleep(2);
return 0;
}
运行结果如图: