多线程编程(二)——线程结束后的处理&主服务存活方法

应用场景:

服务器,创建了多个服务子线程,而后主线程“无所事事”,进程会被关闭,导致子线程sleepA和sleepB无法正常执行完成。(假设sleepA()和sleepB()都是沉睡若干秒的函数,这个肯定要比程序执行到main结束那几步所花费时间要长)

int main()
{
        int iRet = 0;
        iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL);
        if(iRet != 0)
        {
                printf("create failed!\n");
        }

        iRet = pthread_create(&thread_b_id,NULL,sleepB,NULL);
        if(iRet != 0)
        {
                printf("create failed!\n");
        }

}


想让sleepA和sleepB能够正常持续运行下去,进程必须赖下去——让主线程不停止。低劣的做法如下:

int main()
{
        int iRet = 0;
        iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL);
        if(iRet != 0)
        {
                printf("create failed!\n");
        }

        iRet = pthread_create(&thread_b_id,NULL,sleepB,NULL);
        if(iRet != 0)
        {
                printf("create failed!\n");
        }
while(1)
{
sleep(60);
}

}

提倡的做法如下:

int main()
{
        int iRet = 0;
        iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL);
        if(iRet != 0)
        {
                printf("create failed!\n");
        }

        iRet = pthread_create(&thread_b_id,NULL,sleepB,NULL);
        if(iRet != 0)
        {
                printf("create failed!\n");
        }
        pthread_join(thread_a_id,NULL);
        pthread_join(thread_b_id,NULL);

}


这个方法用了pthread_join()等待指定的服务线程结束并释放资源,注意,是阻塞的等待!因为不会向下运行,所以主服务得以存活。因为是阻塞的,在join a的时候就绝对不会join b了,所以如上代码如果b先结束,程序也不会前进,等待a结束才会向下执行。


这里注意,默认情况新建线程是joinable的,可以用join,如果设置成别的,也会join不成。

============================================================================

另外,这个join之后不一定就主进程结束,这样用就太单纯了。

应用一:

比如本来要持续运行服务的子线程,可以pthread_join()之后再pthread_create(),这样就能起到内部容错和保活的作用。

int main()
{
        int iRet = 0;
        iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL);
        if(iRet != 0)
        {
                printf("create failed!\n");
        }

        printf("before join a\n");
        pthread_join(thread_a_id,NULL);
        printf("after join a\n");
        iRet = pthread_create(&thread_a1_id,NULL,sleepA,NULL);
        if(iRet != 0)
        {
                printf("create failed!\n");
        }

}


但这只是想象,实际上并不能成功,问题在哪呢?

# ./a.out
before join a
sleepA called!
now sleepA over!
after join a
可以看到,程序不可能在join之后直接终结,后续流程还是要走,但是却并没有第二次运行sleepA(),如果只是第二次pthread_create失败,也并没有打印失败。

关于id值,使用和之前相同的thread_a_id或者单独的一个thread_a1_id结果都是一样的不成功!

关于函数,使用sleepA或者sleepB都一样的不成功,所以也和函数没关系!

所以,pthread_join()之后到底算个什么状态?为什么ptread_create不行了?


其实,上边是我故意做的错误示范安静,第二次pthread_create()之后主线程结束了啊,缺少第二个join来阻塞住进程啊!!!!!!后根一个该id对应的join操作,解决!

# ./a.out
before join a
sleepA called!
now sleepA over!
after join a
now sleepB over!

============================================================

应用二:

但是如果再次create的代码在后边添加,不能实现持续的容错重启,所以可以改成一个大循环,内部先pthread_create(),再pthread_join()阻塞,循环结束,再pthread_create(),再pthread_join()。。。。。。如此往复。

int main()
{
        int iRet = 0;
        while(1)
{
        iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL);
        if(iRet != 0)
        {
                printf("create failed!\n");
        }

        printf("before join a\n");
        pthread_join(thread_a_id,NULL);
        printf("after join a\n");
}

}

运行结果如下,不强制关闭不会停。
# ./a.out
before join a
sleepA called!
now sleepA over!
after join a
before join a
sleepA called!
now sleepA over!
after join a
before join a
sleepA called!
now sleepA over!
after join a
before join a
sleepA called!
now sleepA over!
after join a
before join a
sleepA called!
^C


应用三:

这个线程的处理还可以这样用,在一个大循环内,等待信号,有一个请求到来就新建一个线程单独去处理这个请求,然后结束再回来。(这里假设a是信号量,是一个全局变量,有一个单独的线程传信号——改a的值,通过信号量a来触发服务sleepA)

#include <stdio.h>
#include <pthread.h>
static pthread_t thread_a_id;
static pthread_t thread_a1_id;
static pthread_t thread_b_id;
static int i = 0;
static int a = 0;
void* sleepA(void *p)
{
        printf("sleepA called!\n");
        sleep(1);
        printf("now sleepA over!\n");
        return NULL;
}
void* setA(void *p)
{
        while(1)
        {
                sleep(2);
                a = 1;
        }
        return NULL;
}
int main()
{
        int iRet = 0;
        iRet = pthread_create(&thread_b_id,NULL,setA,NULL);
        if(iRet != 0)
        {
                printf("create failed!\n");
        }

        while(1)
        {
        if(a == 1)
        {

                iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL);
                if(iRet != 0)
                {
                        printf("create failed!\n");
                }
                printf("before join a\n");
                pthread_join(thread_a_id,NULL);
                printf("after join a\n");
                a = 0;

        }
        }

}

如上,两秒一置1,sleepA只睡1秒,还忙得过来,假如1秒一置1,sleepA睡2秒呢?睡5秒呢?代码变动如下

void* sleepA(void *p)
{
        printf("sleepA called!\n");
        sleep(5);
        printf("now sleepA over!\n");
        return NULL;
}
void* setA(void *p)
{
        while(1)
        {
                sleep(1);
                a = 1;
        }
        return NULL;
}

明显会慢得多!!!因为pthread_join()是阻塞的,这样一次只可能有一个线程去处理请求,不能达到想要的效果。


应用四:

这时候就不能用pthread_join()了,要用pthread_detach(),这个非阻塞,等于生完孩子就不管了,放养,最后也能回收线程资源(这些join和detach什么的,主要就是等着回收资源)。

#include <stdio.h>
#include <pthread.h>
static pthread_t thread_a_id;
static pthread_t thread_a1_id;
static pthread_t thread_b_id;
static int i = 0;
static int a = 0;
void* sleepA(void *p)
{
        printf("sleepA called!\n");
        sleep(5);
        printf("now sleepA over!\n");
        return NULL;
}
void* setA(void *p)
{
        while(1)
        {
                sleep(1);
                a = 1;
        }
        return NULL;
}
int main()
{
        int iRet = 0;
        iRet = pthread_create(&thread_b_id,NULL,setA,NULL);
        if(iRet != 0)
        {
                printf("create failed!\n");
        }

        while(1)
        {
        if(a == 1)
        {

                iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL);
                if(iRet != 0)
                {
                        printf("create failed!\n");
                }
                printf("before detach a\n");
                pthread_detach(thread_a_id);
                printf("after detach a\n");

                a = 0;
        }
        }

}



pthread_detach(),这个在子线程(pthread_self())中和父线程(child_thread_id)中用法略有不同,但本质一样,参数都是要detach的那个线程的id。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值