linux线程终止_linux一个线程终止另一个线程 - CSDN
精华内容
参与话题
  • Linux:线程&线程创建&线程终止

    千次阅读 2018-04-09 19:51:17
    一个进程可以并发线程,每个线程执行不同的任务 比如四个人在一个房子里打麻将,创建一个新进程就相当于是在开一个房间,而创建一个新线程是在原有的房间中增加一个人而已 线程的优点 1.创建一个新的...

    线程

    1.是操作系统能够进行调度的最小单位
    2.线程被包含在进程之中,是进程中的实际运作单位
    3.一个线程指的是进程中一个单一顺序的控制流
    4.一个进程可以并发多个线程,每个线程执行不同的任务

    比如四个人在一个房子里打麻将,创建一个新进程就相当于是在开一个房间,而创建一个新线程是在原有的房间中增加一个人而已
    这里写图片描述

    线程的优点

    1.创建一个新的线程的代价要比创建一个新进程的代价小得多
    2.与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
    3.线程占有的资源要比进程少
    4.线程之间共享数据更容易(线程是在一个房间中,所以相互通信比较容易,而进程之间通话需要跑到另一个房间)

    线程的缺点

    1.编码/调试难度提高
    因为线程之间谁先执行不确定,在一个是共享资源的问题
    2.缺乏访问控制
    一个线程崩溃,会导致整个进程都异常终止,一个线程中调用某些函数,会影响整个进程

    进程与线程的区别

    1.进程:进程(或者任务)是资源分配的基本单位
    2.线程:线程(或者轻量级进程)是调度/执行的基本单位
    3.线程运行在进程中
    4.一个进程至少都有一个线程

    进程与线程中函数的对比

    这里写图片描述
    为什么线程/进程数不是越多越好?

    1.线程/进程数目达到一定程度时,就不会再提高程序的执行效率了
    2.线程/进程数目过多可能会导致进程异常终止
    3.线程/进程数目过多可能会导致线程安全问题(线程安全:多个线程竞争同一资源)

    线程创建

    pthread_create

    功能:创建一个新的线程
    头文件:#include<pthread.h>
    原型:
     int pthread_create(   pthread_t *pthread, 
                           const pthread_attr_t*attr, 
                           void*(*start_routine)(void*), 
                           void *arg  )
    参数说明:
    thread:返回线程ID,此参数是一个输出型参数
          (返回的是新创建的线程的ID)
    
    attr:设置线程的属性,attr为NULL表示使用默认属性(一般设为NULL)
        (设置的是新线程的属性)
    
    start_routine:是个函数地址,线程启动后需要执行的函数
                 (这是一个函数指针,指向线程的入口函数)
    
    arg:传给线程启动函数的参数
    
    返回值:成功返回0,失败返回错误码
    

    线程被创建后,并不能保证那个线程先执行,新创建的线程和调用线程的执行顺序不确定,由操作系统进行调度

    示例

    1.创建一个线程
    代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<pthread.h>
    void*ThreadEntry(void*arg)
    {
        (void)arg;
        printf("I am thread\n");
    }
    
    int main()
    {
        pthread_t tid;
        int ret=0;
        ret=pthread_create(&tid,NULL,ThreadEntry,NULL);
        if(ret!=0)
        {
            perror("pthread_create");
            exit(1);
        }
        printf("I am main\n");
        sleep(1);
     //让主线程睡眠1秒,是为了不让此线程跑的太快使整个进程结束
     //如果主线程跑的太快,新创建的线程还没来的及执行,整个进程就已经结束了
        return 0;
    }
    

    运行结果:
    这里写图片描述

    2.使用死循环来创建线程(死循环是为了防止主线程跑的太快,而使新创建的线程没有机会执行,让它循环多次相当于是等待新线程启动和执行)
    代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<pthread.h>
    void*ThreadEntry(void*arg)
    {
        (void)arg;
        while(1)
        {
            printf("I am thread,%1x\n",pthread_self());
            sleep(1);
        }
    }
    int main()
    {
        int ret=0;
        pthread_t tid;
        ret=pthread_create(&tid,NULL,ThreadEntry,NULL);
        if(ret!=0)
        { 
            perror("pthread_create");
            exit(1);
        }
        while(1)
        {
            printf("I am main,%1x\n",pthread_self());
            sleep(1);
        }
        return 0;
    }

    pthread_self()是得到当前线程的ID

    线程创建函数在libpthread.so库中,所以在编译时需要将该库导入,命令如下:
    例如编译create.c
    gcc create.c -lpthread
    就是说在编译时需要加上 "-lpthread"

    运行结果:
    这里写图片描述

    线程终止

    如果需要终止某个线程,而不终止整个进程,有以下三种方法:

    1.从需要终止的线程函数中直接return
    ps:这种方法对主线程不适用,因为从main函数return相当于调用exit,会使main函数直接退出
    2.需要终止的线程可以调用pthread_exit()来终止自己
    3.一个线程可以调用pthread_cancel来终止同一进程中的其他线程

    pthread_exit()

    
    1.功能:终止线程
    
    2.原型:
    void pthread_exit(void *value_ptr)
    参数说明:
    value_ptr:不要指向一个局部变量
    
    3.返回值:
    无返回值
    

    pthread_cancel()

    1.功能:取消一个执行中的线程
    
    2.原型:
    int pthread_cancel(pthread_t thread)
    参数说明:
    thread:需要取消的线程的ID
    
    3.返回值:
    成功返回0,失败返回错误码
    

    1.直接return

    #include<unistd.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<pthread.h>
    void*ThreadEntry(void*arg)
    {
        int count=0;
        (void)arg;
        while(1)
        {
            ++count;
            if(count>=5)
                return NULL;
            printf("I am thread,%1x\n",pthread_self());
            sleep(1);
        }
    }
    int main()
    {
    int ret=0;
        pthread_t tid;
    ret=    pthread_create(&tid,NULL,ThreadEntry,NULL);
    if(ret!=0)
    {
     perror("pthread_create");
     exit(1);
     }
        while(1)
        {
            printf("I am main,%1x\n",pthread_self());
            sleep(1);
        }
        return 0;
    }

    运行结果:
    这里写图片描述

    2.调用pthread_exit()

    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<pthread.h>
    void*ThreadEntry(void *arg)
    {
        int count=0;
        (void)arg;
        while(1)
        {
            ++count;
            if(count>=7)
                pthread_exit(NULL);
            printf("I am thread\n");
            sleep(1);
        }
    }
    int main()
    {
        int ret=0;
        pthread_t tid;
        ret=pthread_create(&tid,NULL,ThreadEntry,NULL);
        if(ret!=0)
        {
            perror("ptherad_create");
            exit(1);
        }
        while(1)
        {
            printf("I am main\n");
            sleep(1);
        }
        return 0;
    }
    

    运行结果:
    这里写图片描述

    3.调用pthread_cancel()

    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<pthread.h>
    void *ThreadEntry(void*arg)
    {
        (void)arg;
        while(1)
        {
            printf("I am thread\n");
            sleep(1);
        }
    }
    int main()
    {
        int ret=0;
        int count=0;
        pthread_t tid;
        ret=pthread_create(&tid,NULL,ThreadEntry,NULL);
        if(ret!=0)
        {
            perror("pthread_create");
            exit(1);
        }
        while(1)
        {
            ++count;
            if(count>10)
                pthread_cancel(tid);
            printf("I am main\n");
            sleep(1);
        }
        return 0;
    }

    运行结果:
    这里写图片描述

    pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用manlloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数就已经退出了
    示例

    1.return返回线程函数栈上分配的变量

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<pthread.h>
    void*ThreadEntry(void*arg)
    {
        (void)arg;
        int a=10;
        int *p=&a;
        printf("I am thread\n");
        return (void*)p;
    }
    
    int main()
    {
        pthread_t tid;
        void*ret;
        int thread=0;
        thread=pthread_create(&tid,NULL,ThreadEntry,NULL);
        if(thread!=0)
        {
            perror("pthread_create");
            exit(1);
        }
        printf("I am main\n");
        pthread_join(tid,&ret);
        printf("ret=%d\n",*(int*)ret);
        return 0;
    }

    这里写图片描述

    2.pthread_exit中的参数是线程函数在栈上分配的

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<pthread.h>
    void*ThreadEntry(void*arg)
    {
        (void)arg;
        int count=0;
        int a=10;
        int *p=&a;
        while(1)
        {
            ++count;
            if(count>=5)
                pthread_exit(p);
            printf("I am thread\n");
            sleep(1);
        }
    }
    
    int main()
    {
        pthread_t tid;
        void*ret;
        int thread=0;
        thread=pthread_create(&tid,NULL,ThreadEntry,NULL);
        if(thread!=0)
        {
            perror("pthread_create");
            exit(1);
        }
        printf("I am main\n");
        pthread_join(tid,&ret);
        printf("ret=%d\n",*(int*)ret);
        return 0;
    }

    这里写图片描述

    malloc分配

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<pthread.h>
    void*ThreadEntry(void*arg)
    {
        (void)arg;
        int count=0;
        int *p=(int*)malloc(sizeof(int));
        *p=10;
        while(1)
        {
            ++count;
            if(count>=5)
                pthread_exit(p);
            printf("I am thread\n");
            sleep(1);
        }
    }
    
    int main()
    {
        pthread_t tid;
        void*ret;
        int thread=0;
        thread=pthread_create(&tid,NULL,ThreadEntry,NULL);
        if(thread!=0)
        {
            perror("pthread_create");
            exit(1);
        }
        printf("I am main\n");
        pthread_join(tid,&ret);
        printf("ret=%d\n",*(int*)ret);
        free(ret);//malloc后记得释放
        return 0;
    }

    这里写图片描述

    全局变量

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<pthread.h>
    int a=20;
    void*ThreadEntry(void*arg)
    {
        (void)arg;
        int count=0;
        int*p=&a;
        while(1)
        {
            ++count;
            if(count>=5)
                pthread_exit(p);
            printf("I am thread\n");
            sleep(1);
        }
    }
    
    int main()
    {
        pthread_t tid;
        void*ret;
        int thread=0;
        thread=pthread_create(&tid,NULL,ThreadEntry,NULL);
        if(thread!=0)
        {
            perror("pthread_create");
            exit(1);
        }
        printf("I am main\n");
        pthread_join(tid,&ret);
        printf("ret=%d\n",*(int*)ret);
        return 0;
    }
    

    这里写图片描述

    展开全文
  • 线程终止: 1、 如果进程中的任意一个线程调用了exit,_Exit,_exit,那么整个进程就会终止 2、 从启动例程中返回,返回值是线程的退出码 3、 线程可以被同一进程中的其他线程取消 4、 线程调用pthread_exit(void *...

    线程终止:

    1、 如果进程中的任意一个线程调用了exit,_Exit,_exit,那么整个进程就会终止

    2、 从启动例程中返回,返回值是线程的退出码

    3、 线程可以被同一进程中的其他线程取消

    4、 线程调用pthread_exit(void *rval)函数,rval是退出码


    #include <stdio.h>
    #include <iostream>
    #include <pthread.h>
    #include <stdlib.h>
    #include <string.h>
    using namespace std;
    
    int method = 1;
    
    void *thread_fun(void *arg)
    {
    	//如果传到参数1,那么就采用return方式退出
    	if(method==1)
    	{
    		cout<<"new thread using return!"<<endl;
    		return (void *)1;
    	}
    	//如果传入参数2,那么就采用pthreade_xit方式退出
    	if(method==2)
    	{
    		cout<<"new thread using pthread_exit!"<<endl;
    		pthread_exit((void *)2);
    	}
    	//如果传入参数3,那么就采用exit方式退出
    	if(method==3)
    	{
    		cout<<"new thread using exit!"<<endl;
    		exit(3);
    	}
    	return 0;
    }
    
    int main(int argc, char *argv[])
    {
    	int err;
    	pthread_t tid;
    
    	err = pthread_create(&tid, NULL, thread_fun, (void *)argv[1]);
    	if(err != 0)
    	{
    		cout<<"create new  thread failed"<<endl;
    		return 0;
    	}
    	void *status;
    	pthread_join(tid, &status);
    	cout<<"main thread"<<endl;
    	cout<<"new thread's exit status: "<<(int)status<<endl;
    	return 0;
    }
    



    线程连接:

    int pthread_join(pthead_ttid, void **rval)

    调用该函数的线程会一直阻塞,直到指定的线程tid调用pthread_exit、从启动例程返回或者被取消

    参数tid就是指定线程的id

    参数rval是指定线程的返回码,如果线程被取消,那么rval被置为PTHREAD_CANCELED

    该函数调用成功会返回0,失败返回错误码

     

    调用pthread_join会使指定的线程处于分离状态,如果指定线程已经处于分离状态,那么调用就会失败

    pthread_detach可以分离一个线程,线程可以自己分离自己

    intpthread_detach(pthread_t thread);

    成功返回0,失败返回错误码


    #include <stdio.h>
    #include <iostream>
    #include <pthread.h>
    
    using namespace std;
    
    void *thread_fun1(void *arg)
    {
    	cout<<"thread 1 is runing"<<endl;
    	return (void *)1;
    }
    
    void *thread_fun2(void *arg)
    {
    	cout<<"thread 2 is runing"<<endl;
    	pthread_detach(pthread_self());
    	pthread_exit((void *)2);
    }
    
    int main()
    {
    	int err1, err2;
    	pthread_t tid1, tid2;
    	void *rval1, *rval2;
    
    	err1 = pthread_create(&tid1, NULL, thread_fun1, NULL);
    	err2 = pthread_create(&tid2, NULL, thread_fun2, NULL);
    
    	if(err1 || err2)
    	{
    		cout<<"create new thread failed"<<endl;
    		return 0;
    	}
    
    	cout<<"main is runing"<<endl;
    
    	cout<<"1 join err code is: "<<pthread_join(tid1, &rval1)<<endl;
    	cout<<"2 join err code is: "<<pthread_join(tid2, &rval2)<<endl;
    
    	cout<<"thread 1 return code is: "<<std::oct<<(int)(int *)rval1<<endl;
    	cout<<"thread 2 return code is: "<<std::oct<<(int)(int *)rval2<<endl;
    
    	cout<<"main is ended"<<endl;
    
    	return 0;
    
    }



    线程1连接正常,线程二由于分离后释放资源导致无法连接返回错误码22,return值也没有成功获取。


    线程取消

    取消函数原型

    int pthread_cancle(pthread_ttid)

    取消tid指定的线程,成功返回0。取消只是发送一个请求,并不意味着等待线程终止,而且发送成功也不意味着tid一定会终止

    用于取消一个线程,它通常需要被取消线程的配合。线程在很多时候会查看自己是否有取消请求。

    如果有就主动退出,这些查看是否有取消的地方称为取消点。

    取消状态,就是线程对取消信号的处理方式,忽略或者响应。线程创建时默认响应取消信号

     设置取消状态函数原型

    int pthread_setcancelstate(int state, int*oldstate) 

    设置本线程对Cancel信号的反应,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则存入运来的取消动作类型值。

     

    取消一个线程,它通常需要被取消线程的配合。线程在很多时候会查看自己是否有取消请求

    如果有就主动退出,这些查看是否有取消的地方称为取消点

     

    很多地方都是包含取消点,包括

    pthread_join()、pthread_testcancel()、pthread_cond_wait()、 pthread_cond_timedwait()、sem_wait()、sigwait()、write、read,大多数会阻塞的系统调用


    #include <stdio.h>
    #include <iostream>
    #include <pthread.h>
    #include <stdlib.h>
    #include <string.h>
    using namespace std;
    
    void *thread_fun(void *arg)
    {
    	int stateval;
    	int typeval=0;
    	//设置线程disable取消,此操作有点类似关中断,哈哈
    	stateval = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    	if(stateval != 0)
    	{
    		printf("set cancel state failed\n");
    	}
    
    	printf("Im new thread,wait 4s\n");//
    
    	sleep(4);//在等待的4s时间里主线程调用取消函数
    
    	printf("about to cancel \n");
    	//设置线程enable取消
    	stateval = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    	if(stateval != 0)
    	{
    		printf("set cancel state failed\n");
    	}
    	typeval = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    //	typeval = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
    	if(typeval != 0)
    	{
    		printf("set cancel type failed\n");
    	}
    
    	printf("first cancel point\n");
    	printf("second cancel point\n");
    
    	return (void *)20;
    }
    
    int main()
    {
    	pthread_t tid ;
    	int err, cval, jval;
    	void *rval;
    
    	err = pthread_create(&tid, NULL, thread_fun, NULL);
    	if(err != 0)
    	{
    		printf("create thread failed\n");
    		return 0;
    	}
    	sleep(2);
    	printf("canceling new thread!\n");
    	cval = pthread_cancel(tid);//等待两秒取消子线程
    	if(cval != 0)
    	{
    		printf("cancel thread failed\n");
    	}
    	jval = pthread_join(tid, &rval);
    
    	printf("new thread exit code is %d\n", (int *)rval);
    
    	return 0;
    }



    展开全文
  • 主动终止终止自己) 调用 return (void*)var; 调用void pthread_exit(void *value_ptr), 其它线程可以调用 pthread_join 获得这个针。 注: 这两个函数返回值都为指针类型,在线程中使用时所指向的地址必须为...
    • 主动终止(终止自己)

    调用 return (void*)var;
    调用void pthread_exit(void *value_ptr), 其它线程可以调用 pthread_join 获得这个针。

    注:
    这两个函数返回值都为指针类型,在线程中使用时所指向的地址必须为:malloc分配的或者全局变量;因为当线程退出是 其作用域的变量都将消失。
    这两个函数的返回值都能通过 int pthread_join(pthread_t thread, void **value_ptr) 获取,线程结束但线程所占用的资源并 没有立即释放调用该函数的线程将挂起等待, 等到线程结束且所占用的资源也就释放了。直到id为 thread 的线程终止。
    thread 线程以不同的方法终止,通过 pthread_join 得到的终止状态是不同的,总结如下:

    1. 如果 thread 线程通过 return 返回, value_ptr 所指向的单元里存放的是 thread 线程函数的返回值。
    2. 如果 thread 线程被别的线程调用 pthread_cancel 异常终止掉, value_ptr 所指向的单元里存放的是常数 PTHREAD_CANCELED
    3. 如果 thread 线程是自己调用 pthread_exit 终止的, value_ptr 所指向的单元存放的是传给 pthread_exit 的参数。
    4. 如果对 thread 线程的终止状态不感兴趣,可以传 NULL 给 value_ptr 参数。
    • 被动终止
      个线程可以调用 pthread_cancel 终止同一进程中的另一个线程

    • 综合示例程序

    #include<stdio.h>
    #include<pthread.h>
    #include<stdlib.h>
    #include<unistd.h>
    typedef struct
    {
        int var1;
        int var2;
    }Arg;
    
    void *th_fn1(void *arg)
    {
    	printf("thread 1 returning\n");
    	return (void *)1;
    }
    void *th_fn2(void *arg)
    {
    	printf("thread 2 returning\n");
    	pthread_exit((void *)2);
    }
    void *th_fn3(void *arg)
    {
    	while(1)
    	{
    		printf("thread 3 writing\n");
    		sleep(1);
    	}
    }
    void *th_fn4(void *arg)
    {
        Arg *r=(Arg *)arg;
       
    	printf("thread 1 returning\n");
    	return (void *)r;              //强转
    }
    
    int main(void)
    {
    	pthread_t tid;
    	void *tret;
       Arg r={10,20};
       
    	pthread_create(&tid,NULL,th_fn1,NULL);
    	pthread_join(tid,&tret);
        printf("thread 1 exit code %d\n",(int)tret);
    
    	pthread_create(&tid,NULL,th_fn2,NULL);
    	pthread_join(tid,&tret);
       printf("thread 2 exit code %d\n",(int)tret);   //细节   指针输出printf("%d\n",tret);即可输出tret地址存放的值
    
    	pthread_create(&tid,NULL,th_fn3,NULL);
    	sleep(3);
    	pthread_cancel(tid);
    	pthread_join(tid,&tret);
    	printf("thread 3 exit code %d\n",(int)tret);
     //当执行函数返回自定义指针(arg)时的情况
        thread_create(&tid,NULL,th_fn4,(void *)&r);  //细节传自定义数组
    	pthread_join(tid,(void **)&tret);
        printf("thread 4 exit code %d\n",((Arg *)tret)->var1+((Arg*)tret)->var2);  //强转
    	return 0;
    }
    
    • 运行情况
    thread 1 returning
    thread 1 exit code 1
    thread 2 returning
    thread 2 exit code 2
    thread 3 writing
    thread 3 writing
    thread 3 writing
    thread 3 exit code -1
    thread 1 returning
    thread 4 exit code 30
    
    
    
    展开全文
  • 在使用pthread_cancel和pthread_join退出一个线程时,线程本身会立即停止运行代码并退出吗?如果在你试图退出一个线程时,该线程中还有未释放的资源,这时应该怎么处理呢? 在cancel一个线程时,线程体可能并不会...

    问题背景:
    在使用pthread_cancel和pthread_join退出一个线程时,线程本身会立即停止运行代码并退出吗?如果在你试图退出一个线程时,该线程中还有未释放的资源,这时应该怎么处理呢?

    在cancel一个线程时,线程体可能并不会立即退出,这就会出现两个问题:

    1. 在调用cancel之后,如果线程体仍在运行,就会导致pthread_join()阻塞,进而使调用pthread_join()的线程也被阻塞。当然你也可以用pthread_detach(),但这种让线程自生自灭的方式可能导致一些不可控的情况。
    2. 如果在线程退出时有动态分配的内存未释放,则会产生内存泄漏,如果有锁还没有释放则其他地方就一直拿不到锁了从而导致死锁。

    这两个问题,我们都可以利用线程的取消点(cancellation points)来避免。线程的cancel type有两种:PTHREAD_CANCEL_DEFERRED和PTHREAD_CANCEL_ASYNCHRONOUS,前者为默认类型,意味着线程只有在取消点处才能被cancel。也就是说,在对线程pthread_cancel()之后,线程还要继续执行到下一个取消点才会退出。
    可以通过pthread_setcanceltype()来改变线程的cancel type,但强烈不建议这样做,因为你如果改为PTHREAD_CANCEL_ASYNCHRONOUS类型,线程可以在代码的任何地方退出,就很难处理上述两个资源释放问题。

    所谓“取消点”就是一个函数,例如sleep(),当线程体运行到sleep()的时候,就可以响应cancel了。这样一来,我们可以这样做:

    1. 在代码的适当位置主动插入取消点,主要为了防止耗时操作(例如比较大的while循环)导致线程无法退出。
    2. 在需要进行同步操作时不设置取消点,例如在加锁和解锁之间、线程内存申请的内存free之前,或者你代码逻辑中必须做完某事才能退出的。这样你就不用担心线程退出前还有未达成的遗愿。

    除了通过设置取消点的方式来控制线程退出之外,还有一对函数:pthread_cleanup_push() /pthread_cleanup_pop(),可以让你单独注册一个清理函数。但这种方式没有取消点来的简单,因为在线程不同地方要做的清理可能也不相同。

    不过,我的建议是,尽量创建常驻线程,让它一直存在,没事做就等着,省了在取消点上伤脑筋。
    更多多线程的编程指南,可参考https://docs.oracle.com/cd/E19455-01/806-5257/index.html

    至于哪些函数可作为取消点,大家可参考 https://linux.die.net/man/7/pthreads 中的Cancellation points一节。

    下面转载的两篇文章介绍了如何选择取消点(cancellation points)以及如何清理线程资源,原文分别为:
    https://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part4/
    http://blog.csdn.net/shinichr/article/details/24271675
    下面为转载内容:
    ——————————

    线程终止方式

    一般来说,Posix的线程终止有两种情况:正常终止和非正常终止。线程主动调用pthread_exit()或者从线程函数中return都将使线程正常退出,这是可预见的退出方式;非正常终止是线程在其他线程的干预下,或者由于自身运行出错(比如访问非法地址)而退出,这种退出方式是不可预见的。

    线程终止时的清理

    不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,在不考虑因运行出错而退出的前提下,如何保证线程终止时能顺利的释放掉自己所占用的资源,特别是锁资源,就是一个必须考虑解决的问题。
    最经常出现的情形是资源独占锁的使用:线程为了访问临界资源而为其加上锁,但在访问过程中被外界取消,如果线程处于响应取消状态,且采用异步方式响应,或者在打开独占锁以前的运行路径上存在取消点,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可预见的,因此的确需要一个机制来简化用于资源释放的编程。
    在POSIX线程API中提供了一个pthread_cleanup_push()/pthread_cleanup_pop()函数对用于自动释放资源–从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用pthread_exit()和取消点终止)都将执行pthread_cleanup_push()所指定的清理函数。API定义如下:

    void pthread_cleanup_push(void (*routine) (void  *),  void *arg)
    void pthread_cleanup_pop(int execute)

    pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理,void routine(void *arg)函数在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push()的调用将在清理函数栈中形成一个函数链,在执行该函数链时按照压栈的相反顺序弹出。execute参数表示执行到pthread_cleanup_pop()时是否在弹出清理函数的同时执行该函数,为0表示不执行,非0为执行;这个参数并不影响异常终止时清理函数的执行。
    pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义:

    #define pthread_cleanup_push(routine,arg)                                     \
      { struct _pthread_cleanup_buffer _buffer;                                   \
        _pthread_cleanup_push (&_buffer, (routine), (arg));
    #define pthread_cleanup_pop(execute)                                          \
        _pthread_cleanup_pop (&_buffer, (execute)); }

    可见,pthread_cleanup_push()带有一个”{“,而pthread_cleanup_pop()带有一个”}”,因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。在下面的例子里,当线程在”do some work”中终止时,将主动调用pthread_mutex_unlock(mut),以完成解锁动作。

    pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
    pthread_mutex_lock(&mut);
    /* do some work */
    pthread_mutex_unlock(&mut);
    pthread_cleanup_pop(0);

    必须要注意的是,如果线程处于PTHREAD_CANCEL_ASYNCHRONOUS状态,上述代码段就有可能出错,因为CANCEL事件有可能在pthread_cleanup_push()和pthread_mutex_lock()之间发生,或者在pthread_mutex_unlock()和pthread_cleanup_pop()之间发生,从而导致清理函数unlock一个并没有加锁的mutex变量,造成错误。因此,在使用清理函数的时候,都应该暂时设置成PTHREAD_CANCEL_DEFERRED模式。为此,POSIX的Linux实现中还提供了一对不保证可移植的pthread_cleanup_push_defer_np()/pthread_cleanup_pop_defer_np()扩展函数,功能与以下代码段相当:

    { int oldtype;
     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
     pthread_cleanup_push(routine, arg);
     ...
     pthread_cleanup_pop(execute);
     pthread_setcanceltype(oldtype, NULL);
     }

    线程终止的同步及其返回值

    一般情况下,进程中各个线程的运行都是相互独立的,线程的终止并不会通知,也不会影响其他线程,终止的线程所占用的资源也并不会随着线程的终止而得到释放。正如进程之间可以用wait()系统调用来同步终止并释放资源一样,线程之间也有类似机制,那就是pthread_join()函数。

    void pthread_exit(void *retval) 
    int pthread_join(pthread_t th, void **thread_return)
    int pthread_detach(pthread_t th)

    pthread_join()的调用者将挂起并等待th线程终止,retval是pthread_exit()调用者线程(线程ID为th)的返回值,如果thread_return不为NULL,则*thread_return=retval。需要注意的是一个线程仅允许唯一的一个线程使用pthread_join()等待它的终止,并且被等待的线程应该处于可join状态,即非DETACHED状态。
    如果进程中的某个线程执行了pthread_detach(th),则th线程将处于DETACHED状态,这使得th线程在结束运行时自行释放所占用的内存资源,同时也无法由pthread_join()同步,pthread_detach()执行之后,对th请求pthread_join()将返回错误。
    一个可join的线程所占用的内存仅当有线程对其执行了pthread_join()后才会释放,因此为了避免内存泄漏,所有线程的终止,要么已设为DETACHED,要么就需要使用pthread_join()来回收。

    关于pthread_exit()和return

    理论上说,pthread_exit()和线程宿体函数退出的功能是相同的,函数结束时会在内部自动调用pthread_exit()来清理线程相关的资源。但实际上二者由于编译器的处理有很大的不同。
    在进程主函数(main())中调用pthread_exit(),只会使主函数所在的线程(可以说是进程的主线程)退出;而如果是return,编译器将使其调用进程退出的代码(如_exit()),从而导致进程及其所有线程结束运行。
    其次,在线程宿主函数中主动调用return,如果return语句包含在pthread_cleanup_push()/pthread_cleanup_pop()对中,则不会引起清理函数的执行,反而会导致segment fault。

    ——————————
    线程取消:
    取消操作允许线程请求终止其所在进程总的任何其他线程。不需要线程执行进一步操作时,可以选择取消操作。
    取消点:
    如果线程模式设置的是异步模式的话,那只有到取消点才会取消线程。下面会讲到两种取消方式。
    那取消点有哪些呢?
    1:通过pthread_testcancel 调用已编程方式建立线程取消点。
    2:线程等待pthread_cond_wait或pthread_cond_timewait中的特定条件。
    3:被sigwait(2)阻塞的函数。
    4:一些标准的库调用。通常这些调用包括线程可基于阻塞的函数。
    default情况下,会启用取消功能。
    POSIX标准中,pthread_join(), pthread_testcancel(), pthread_cond_wait(), pthread_cond_timewait(), sem_wait(), sigwait() 等函数以及read,write等会引起阻塞的系统调用。

    取消线程函数
    int pthread_cancel(pthread_t thread);
    成功之后返回0,失败返回错误号,错误号说明如下:
    ESRCH:没有找到线程ID相对应的线程。
    int phread_setcancelstate(int state,int *oldstate);设置本线程对信号的反应:
    状态:
    PTHREAD_CANCEL_ENABLE 默认,收到cancel信号马上设置退出状态
    PTHREAD_CANCEL_DISABLE 很明显不处理cancel
    返回值:
    成功之后返回0.失败返回错误号,错误号说明如下:
    EINVAL:状态不是PTHREAD_CANCEL_ENABLE或者PTHREAD_CANCEL_DISABLE
    那取消方式呢?取消方式有两种,当然只有在PTHREAD_CANCEL_ENABLE状态下有效
    int pthread_setcanceltype(int type, int *oldtype);
    PTHREAD_CANCEL_ASYNCHRONOUS 立即执行取消信号
    PTHREAD_CANCEL_DEFERRED 运行到下一个取消点
    有什么区别呢?
    在延迟模式(PTHREAD_CANCEL_DEFERRED)下,只能在取消点取消线程,在异步模式下,可以在任意一点取消线程,这建立还是用延迟模式
    注意:当取消线程后,线程里面的锁可能还没有unlock,那么就会出现死锁的情况,如何避免呢?
    这里介绍下清理处理程序,它可以将状态恢复到与起点一致的状态,其中包括清理已分配的资源和恢复不变量。
    使用pthread_cleanup_push和pthread_cleanup_pop,
    push函数是将处理程序推送到清理栈,执行顺序自然是FIFO
    void pthread_cleanup_push(void(routine)(void ) , void (args);
    有了这个清理函数,我们只需要把线程要清理的函数push上去,把unlock放在里面,就不会死锁了,当然之前还要lock一下mutex。
    void cleanup(void *arg)
    {
    pthread_mutex_unlock(&mutex);
    }
    这里举个取消点的例子:

    #include <pthread.h>  
    #include <stdio.h>  
    #include <unistd.h>  
    
    void* thr(void* arg)  
    {  
    
             pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);  
             pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);  
    
             while(1)  
             {  
                 pthread_testcancel();  
    
             }  
             printf("thread is not running\n");  
             sleep(2);  
    }  
    int main()  
    {  
             pthread_t tid;  
             int err;  
             err = pthread_create(&tid,NULL,thr,NULL);  
             pthread_cancel(tid);  
             pthread_join(tid,NULL);  
             sleep(1);  
             printf("Main thread exited\n");  
             return 0;  
    }  
    展开全文
  • 一、主动终止 线程的执行函数中调用return语句 调用pthread_exit() 二、被动终止 1、线程可以被同一进程...2、线程终止的3种方式 #include int pthread_cancel(pthread_t tid); void pthread_exit(void *retval); i
  • linux线程结束

    2013-09-19 18:14:37
    Linux线程有三种结束的方法: 1. 线程函数执行完毕,线程正常结束; 2. 线程调用pthread_exit(void* rval_ptr) 3. 线程被取消(也就是其他线程调用pthread_cancel(pthread_t) 这里主要总结一下线程调用pthread_...
  • Linux线程终止

    2013-06-20 10:18:35
    Linux线程终止  如果进程中任一线程调用exit, _Exit, _exit, 整个进程终止. 这不是线程终止, 而是进程, 这将终止该进程中的所有线程.   1. 线程终止的方式:  单个线程可以通过3种方式退出: 从...
  • 1. 什么是线程   线程是进程执行内部的一个执行分支,在一个进程内部运行的多种执行流;内部本质上是线程在同一个地址空间运行;第一个pcb称之为主线程;有线程就有个执行流;一个进程至少有一个线程 ...
  • 一般来说,linx的线程终止有两种情况:正常终止和非正常终止。线程主动调用pthread_exit()或者从线程函数中return都将使线程正 常退出,这是可预见的退出方式;非正常终止是线程在其他线程的干预下,或者由于自身...
  • 1. 什么是线程   线程是进程执行内部的一个执行分支,在一个进程内部运行的多种执行流;内部本质上是线程在同一个地址空间运行;第一个pcb称之为主线程;有线程就有个执行流;一个进程至少有一个线程 ...
  • 线程,我自己看到的两篇讲解比较生动形象的易于理解的博文,如下。 线程与进程之间的关系: http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html 对线程的解(一篇比较生动形象的讲解): ...
  • Linux 多线程环境下 进程线程终止函数小结 pthread_kill: pthread_kill与kill有区别,是向线程发送signal。,大部分signal的默认动作是终止进程的运行,所以,我们才要用signal()去抓信号并加上处理函数。 int ...
  • Linux多线程详解

    2011-12-19 09:52:35
    线程也被称为轻权进程(lightweight process)。 在传统的UNIX上,一个进程让另一个实体做某个事务是用fork派生子进程的方法处理的。派生子进程的代价比线程要昂贵得多,尤其是在父子进程...Linux系统下的多线程遵循POS
  • linux线程的取消(终止)

    万次阅读 2014-12-30 10:32:13
    关键: pthread_cancel函数发送终止信号 ...一般情况下,线程在其主体函数退出的时候会自动终止,但同时也可以因为接收到另一个线程发来的终止(取消)请求而强制终止。 2 线程取消的语义 线程取消
  • Linux多线程编程(一)---多线程基本编程

    万次阅读 多人点赞 2020-03-19 13:20:49
    线程是指运行中的程序的调度单位。一个线程指的是进程中一个单一顺序的控制流,也被称为轻量级线程。它是系统独立调度和分配的基本单位。...一个进程可以有很多线程,每个线程并行执行不同的任务。
  • 一个线程可以通过此机制向另外一个线程发送结束请求,值得一提的是,接收此请求的线程可以通过本线程的两个属性来决定是否取消以及时同步(延时)取消还是异步(立即)取消。 函数成功返回,并不代表那线程就结束了...
  • Linux多线程编程

    千次阅读 2019-06-21 17:42:56
    作为多任务实现的一种机制,多线程应用得非常广泛,相对于多进程,多线程不仅运行效率高,而且还可以提高系统资源的使用效率。虽然网上关于多线程的讲解已经有一大堆,但出于学习的心态,有必要在这里做一下笔记。 ...
  • Linux线程-终止

    万次阅读 2012-09-19 21:45:38
    在前文讨论了线程创建的一些基本东西,这里讨论有哪些方法可以使线程终止线程终止又是如何与创建所需的参数进行关联的。 一,正常终止  线程在执行完成之后,正常终止。 二,线程取消 2.1 线程取消的定义  ...
  • Linux多线程编程入门

    千次阅读 多人点赞 2017-02-23 15:07:19
    线程基本知识\quad进程是资源管理的基本单元,而线程是系统调度的基本单元,线程是操作系统能够进行调度运算的最小单位,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,...
1 2 3 4 5 ... 20
收藏数 58,072
精华内容 23,228
关键字:

linux线程终止