5.线程创建、取消、退出、清理

681 阅读4分钟

线程非正常终止

  1. 如果主线程退出,全部线程将强行终止
  2. 在子线程调用exit(),将终止整个进程
  3. 终止程序的信号,将导致整体进程终止.(如果某个线程coredump,但影响整个进程) 在多进程程序中,子进程core dump不影响其他进程 在多线程程序中,子线程core dump,整个进程受影响。

终止线程的三种方法

1.线程可以简单的从线程函数中返回,返回值是线程的退出码
2.线程可以被同一进程中的其它线程调用pthread_cancel()取消,主线程中也可以取消子线程
3.在线程函数中,调用pthread_exit(0)退出 return 与pthread_exit的区别就是,如果在子线程的子函数数中return返回到子线程中 如果在子函数中pthread_exit的话,则子线程退出。

#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <iostream>
using namespace std;

/*
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
//                          void *(*start_routine) (void *), void *arg);
//int pthread_join(pthread_t thread, void **retval);

线程非正常终止
1. 如果主线程退出,全部线程将强行终止
2. 在子线程调用exit(),将终止整个进程
3. 终止程序的信号,将导致整体进程终止.(如果某个线程coredump,但影响整个进程)
    在多进程程序中,子进程core dump不影响其他进程
    在多线程程序中,子线程core dump,整个进程受影响。


终止线程的三种方法
1.线程可以简单的从线程函数中返回,返回值是线程的退出码
2.线程可以被同一进程中的其它线程调用pthread_cancel()取消,主线程中也可以取消子线程
3.在线程函数中,调用pthread_exit(0)退出
    return 与pthread_exit的区别就是,如果在子线程的子函数数中return返回到子线程中
    如果在子函数中pthread_exit的话,则子线程退出。
*/
void *thmain1(void *arg);
void *thmain2(void *arg);

int var=0;  //全局变量,所有线程共享进程空间

pthread_t pthid2 = 0;

int main()
{
    pthread_t pthid1 = 0;


    if(pthread_create(&pthid1, NULL, thmain1, NULL) !=0)
    {
        cout <<"create pthid1 faile" << endl;
        exit(-1);
    }

    if(pthread_create(&pthid2, NULL, thmain2, NULL) !=0)
    {
        cout <<"create pthid2 faile" << endl;
        exit(-1);
    }

    //return 0;     //1. 如果主线程退出,全部线程将强行终止      
    // 这里返回的话,没等子线程执行,主线程就退出,什么都没展示
    // 这是跟进程不一样的地方,主进程退出,子进程还可以继续执行


    //等待子线程退出
    cout << "start join..." << endl;
    pthread_join(pthid1,NULL);
    pthread_join(pthid2,NULL);
    cout << "end join" << endl;
}

void *thmain1(void *arg)
{
    for(int i=0; i<50; i++)
    {
        var = i+1;      //线程1改变全局变量的值
        printf("thmain1 sleep(%d) var(%d) ok.\n", i+1, var);
        sleep(1);

        if(i == 5)
        {
            pthread_cancel(pthid2); //2.在线程1中取消线程2
            cout << "cancel pthread2" << endl;
        }
    }
}

void *thmain2(void *arg)        //线程2读出
{
    for(int i=0; i<50; i++)
    {
        printf("thmain2 sleep(%d) var(%d) ok.\n", i+1, var);
        sleep(1);
        //exit(0);    //2. 在子线程调用exit(),将终止整个进程
   
        int* p = new int;
        delete p;
        //delete p;   // 3.重复释放 core;

        //if( i == 2) return 0;     //可以编译通过
        //if( i == 2) return (void *)1; //1.要进行强转,否则编译不通过

        if( i == 2) pthread_exit((void *)1); 
    }
}

线程取消细节

#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <iostream>
using namespace std;

//线程id, typedef unsigned long pthread_t

//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
//                          void *(*start_routine) (void *), void *arg);
//int pthread_join(pthread_t thread, void **retval);


void *thmain1(void *arg);    //线程主函数

int var = 0;

int main()
{
    pthread_t thid1=0, thid2=0;
    if(pthread_create(&thid1, NULL, thmain1, NULL) != 0) {printf("thid1 faile\n"); exit(-1);}

    usleep(100);
    pthread_cancel(thid1);

    void *ret;
    cout << "join..." << endl;
    pthread_join(thid1, &ret);          //主线程等待子线程退出
    printf("thid1 ret=%ld\n", ret);
    printf("join ok\n");

    //如果这里参打印出400000000可能是线程没有被cancel掉,或者在cancel之前,子线程己运行结束
    printf("var=%d\n", var);    
}

void *thmain1( void *arg)
{
    //设置线程为不可取消状态,上面的pthread_cancel()不起作用,第二个参数是保存线程还有的状态
    //pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

    //设置线程可以立即取消,而不是延时取消(默认,除非达有一个取消点出现),主线程100微秒后取消线程,var还未执行4亿次,所以var输出不是4亿
    //pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
    for(var=0; var<400000000; var++)
    {  
        ;
        pthread_testcancel();
        //close(2); 用pthread_testcancel()设置取消点
    }
   return (void*)1;
}

线程退出细节

#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <iostream>
#include <string.h>
using namespace std;

//线程退出(终止)状态
//pthread_join可以获得线程退出状态信息
//线程主函数返回void* 表示,可以返回任务信息

/*终止线程的三种方法
1. 子线程主函数中,直接返回,返回值是线程退出码
2. 子线程可以被同一进程中的其他线程调用pthread_cancel()取消
3. 子线程调用pthread_exit()方法
    return 与pthread_exit的区别就是,如果在子线程的子函数数中return返回到子线程中
    如果在子函数中pthread_exit的话,则子线程退出。

*/

struct st_ret
{
    int retcode;    //返回码
    char retmessage[101];      //返回信息
};


void *thmain1(void *arg);
void *thmain2(void *arg);

int main(int argc, char *argv[])
{
    pthread_t thid1=0;

    //创建线程 
    if(pthread_create(&thid1, NULL, thmain1, NULL) != 0) {printf("thid1 faile\n");}
    
    //等待子线程退出
    cout << "thmain start..." << endl;

    //第二个参数可以获得线程返回信息
    //void *pv =0;
    //pthread_join(thid1, &pv);          //主线程等待子线程退出
    //printf("ret = %d\n", pv);   //子线程return或者pthread_exit((void*)1)返回的值

    struct st_ret *pv = 0;
    pthread_join(thid1, (void**)&pv);
    printf("retcode = %d, retmessage=%s\n", pv->retcode, pv->retmessage);
    delete pv;
    cout << "thmain end " << endl;
    return 0;
}

void *thmain1( void *arg)
{
    printf("thmain1 start...\n");
    //return (void *)1150;              //线程的返回码
    //pthread_exit((void *)1150); //效果同上

    //注意,如果用结构体的地址作为线程的返回值,必须保证在线程主函数结束后地址仍然是有效的
    //所以,要采用动态分配内存的方法,不能用局部变量。
    struct st_ret *st1 = new(struct st_ret);
    st1->retcode = 10;
    strcpy(st1->retmessage, "pthread1");
    return (void *)st1;
}

线程清理细节

#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <iostream>
using namespace std;

//线程id, typedef unsigned long pthread_t

//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
//                          void *(*start_routine) (void *), void *arg);
//int pthread_join(pthread_t thread, void **retval);


void *thmain1(void *arg);    //线程主函数
void thcleanup1(void *arg);      //线程清理函数
void thcleanup2(void *arg);      //线程清理函数
void thcleanup3(void *arg);      //线程清理函数

int main()
{

    pthread_t thid1=0, thid2=0;
    if(pthread_create(&thid1, NULL, thmain1, NULL) != 0) {printf("thid1 faile\n"); exit(-1);}
    
    //sleep(1);
    //pthread_cancel(thid1);     //在这里结束子线程,也一样会执行线程清理函数,返回状态为-1

    void *ret;
    int result = 0;
    cout << "join..." << endl;
    result = pthread_join(thid1, &ret);          //主线程等待子线程退出
    printf("thid1 result=%d, ret=%ld\n", result, ret);
    printf("join ok\n");
}

void *thmain1( void *arg)
{
    pthread_cleanup_push(thcleanup1, NULL); //把线程清理函数入栈,第二个参数是传入线程清理函数的参数
    pthread_cleanup_push(thcleanup2, NULL); //把线程清理函数入栈
    pthread_cleanup_push(thcleanup3, NULL); //把线程清理函数入栈

    //pthread_cleanup_push(thcleanup1, fd); //把线程清理函数入栈,第二个参数是传入线程清理函数的参数
    //pthread_cleanup_push(thcleanup2, sockfd); //把线程清理函数入栈(关闭socket)
    //pthread_cleanup_push(thcleanup3, &conn); //把线程清理函数入栈(回滚数据库事务)

    for(int i=0; i<3; i++)
    {
        //pthread_cleanup_push(thcleanup1, NULL); //把线程清理函数入栈
        sleep(1);
        printf("pthmain1 sleep(%d) ok.\n", i+1);
        //pthread_cleanup_pop(1); //把线程清理函数出栈  这一对push,pop函数一定要成对出现。可以放在语句块中

        //return (void*)1; 在这里结束线程,也会执行线程清理函数。
    }
    pthread_cleanup_pop(3); //把线程清理函数出栈  参数大于0表示出栈并执行清理函数,=0仅表示出栈
    pthread_cleanup_pop(2); //把线程清理函数出栈
    pthread_cleanup_pop(1); //把线程清理函数出栈
    
    //return (void*)1;
    pthread_exit((void*)1);
}

void thcleanup1(void *arg)     //线程清理函数
{
    //在这里释放关闭文件、断开网络连接、回滚数据事务、释放锁等
    printf("thcleanup1 clean ok\n");
}

void thcleanup2(void *arg)     //线程清理函数
{
    //在这里释放关闭文件、断开网络连接、回滚数据事务、释放锁等
    printf("thcleanup2 clean ok\n");
}

void thcleanup3(void *arg)     //线程清理函数
{
    //在这里释放关闭文件、断开网络连接、回滚数据事务、释放锁等
    printf("thcleanup3 clean ok\n");
}

线程资源的回收

回顾:进程资源的回收 1.子进程退出时,向父进程发送SIGCHLD信号 2.父进程调用wait()函数等待子进程退出,忽略SIGCHLD信号 3.在信号处理函数中释放资源

线程资源回收: 1.线程非分离状态joinable(默认状态) 2.线程分离状态(unjoinable) 3.非分离状态的线程终止时,不会释放线程的全部资源

如果不关心线程退出状态,可以把线程的属性设置为分离,线程结束后,由系统回收资源
1.调用pthread_detach()函数分离线程
2.创建线程前,调用pthread_attr_setdetachstate()设置线程的属性
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <iostream>
using namespace std;


//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
//                          void *(*start_routine) (void *), void *arg);
//int pthread_join(pthread_t thread, void **retval);
void *thmain1(void *arg);    //线程主函数
void *thmain2(void *arg);    //线程主函数

int main()
{
    //方法2设置线程分离
    pthread_attr_t attr;        //申明线程属性的数据结构
    pthread_attr_init(&attr);   //初始化
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);    //设置线程的属性


    pthread_t thid1=0, thid2=0;
    if(pthread_create(&thid1, &attr, thmain1, NULL) != 0) {printf("thid1 faile\n"); exit(-1);}
    pthread_attr_destroy(&attr);        //销毁数据结构

    //if(pthread_create(&thid2, NULL, thmain2, NULL) != 0) {printf("thid2 faile\n"); exit(-1);}
    
    sleep(10);

    void *ret;
    int result=0;
    cout << "join..." << endl;

    //主线程等待子线程退出
    result = pthread_join(thid1, &ret); printf("thid1 result=%d,ret=%ld\n", result, ret);  //0,1      
    //result = pthread_join(thid2, &ret); printf("thid2 result=%d,ret=%ld\n", result, ret);  //0,2

    //再次join会失败
    /*
    ret=0;
    result = pthread_join(thid1, &ret); printf("thid1 result=%d,ret=%ld\n", result, ret);  //3,0      
    result = pthread_join(thid2, &ret); printf("thid2 result=%d,ret=%ld\n", result, ret);  //3,0
    */

    cout << "join ok. " << endl;
}

void *thmain1( void *arg)
{
    //pthread_detach(pthread_self());
    for(int i=0; i<3; i++)
    {
        sleep(1);
        printf("pthmain1 sleep(%d) ok.\n", i+1);
    }

   return (void*)1;
}

void *thmain2( void *arg)
{
    pthread_detach(pthread_self());         //设置为分离,则不能再join. 也取不到返回值
    for(int i=0; i<5; i++)
    {
        sleep(1);
        printf("pthmain2 sleep(%d) ok.\n", i+1);
    }

   return (void*)2;
}