多线程之线程的基本控制(含线程的创建和销毁等操作)

一,掌握线程的终止方式,线程的连接,退出操作,清理操作。


二,线程的清理操作是如何进行的?
【注】查看标准函数库的定义解释命令,如: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;
    }

运行结果如图:

发布了50 篇原创文章 · 获赞 13 · 访问量 1831

猜你喜欢

转载自blog.csdn.net/weixin_38251305/article/details/103978162