回收子线程
pthread_join 函数
阻塞等待线程退出,获取线程退出状态 其作用,对应进程中 waitpid()
函数。
int pthread_join (pthread_t thread,void** retval);
成功:0,失败:错误号
参数:thread:线程ID(注意 :不是指针);retval:存储线程结束状态
对比记忆:
- 进程中:main 返回值、exit 参数–>int;等待子进程结束 wait 函数参数–>
int*
- 线程中:线程主函数返回值、
pthread_exit-->void*
;等待线程结束 pthread_join 函数参数–>void**
- 对于进程而言,wait函数的返回值是int,所以获取退出值使用
int*
- 对于线程,
void*
作为函数返回值,回收使用void**
示例1
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>typedef struct{int a;int b;
}exit_t;void * tfn(void *arg) //子线程函数
{//子线程函数中定义 ret;exit_t *ret; //用结构体定义一个变量ret=malloc(sizeof(exit_t));ret->a = 100;ret->b = 300;//返回ret 这个值,线程退出pthread_exit((void *)ret);
}int main(void)
{pthread_t tid;exit_t *retval;pthread_create(&tid,NULL,tfn,NULL);/*调用pthread_join可以获取线程退出状态*///第一个回收线程ID,第二个回收退出的值 pthread_join(tid,(void **)&retval); //wait(&status);printf("a = %d,b = %d\n",retval->a,retval->b);return 0;
}
注意事项
调用该函数的线程将挂起等待,直到 id 为 thread 的线程终止。 thread 线程以不同的方法终止,通过 pthread_join 得到的终止状态是不同的,总结如下:
- 如果 thread 线程通过 return 返回,retval 所指向的单元里存放的是 thread 线程函数的返回值。
- 如果 thread 线程被别的线程调用 pthread_cancel 异常终止掉,retval 所指向的单元里存放的是常数PTHREAD_CANCELED。
- 如果 thread 线程是自己调用 pthread_exit 终止的,retval 所指向的单元存放的是传给 pthread_exit 的参数。
- 如果对 thread 线程的终止状态不感兴趣,可以传 NULL 给 retval 参数。
示例2
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>typedef struct {char ch;int var;char str[64];
}exit_t;void *thrd_func(void *arg)
{//创建结构体变量exit_t * retvar = (exit_t *)malloc(sizeof(exit_t));//赋值retvar->ch='m';retvar->var = 200;strcpy(retvar->str,"我的返回值");//子线程退出pthread_exit((void *)retvar);}int main(void)
{pthread_t tid;int ret;exit_t * retval;//主控线程IDprintf(" In main1 id = %lu,pid = %u\n",pthread_self(),getpid());ret = pthread_create(&tid,NULL,thrd_func,NULL);if(ret != 0){fprintf(stderr,"pthread_create error:%s\n",strerror(ret));exit(1);}pthread_join(tid,(void **)&retval);printf("子线程返回值为\n");printf("ch = %c ,var = %d,str = %s\n",retval->ch,retval->var,retval->str);pthread_exit((void *)1);return 0;
}
使用 pthread_join 函数将循环创建的多个子线程回收。
/*回收多个子线程*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>int var=100;void *tfn(void *arg) //每个子线程进行回收
{int i;i = (int)arg;sleep(i); //输出有顺序if(i == 1){var = 333;printf("var = %d\n",var);return (void *)var;}else if(i == 3){var = 777;printf("I'm %d th 线程,线程ID为 %lu var = %d\n",i+1,pthread_self(),var);pthread_exit((void *)var);}else {printf("I'm %d th 线程,线程ID = %lu\n var = %d\n",i+1,pthread_self(),var);pthread_exit((void *)var);}return NULL;
}int main(void)
{pthread_t tid[5];int i;int *ret[5]; //保存 5个线程的退出值for(i = 0;i < 5; i++)//循环创建多个子线程pthread_create(&tid[i],NULL,tfn,(void*)i);for(i=0;i < 5; i++){ //对多个子线程进行回收pthread_join(tid[i],(void **)&ret[i]);printf("-----------%d th ret = %d\n",i,(int)ret[i]);}printf("I'm main 线程 tid = %lu\t var = %d\n",pthread_self(),var);//主控线程也打印777,原因是共享全局变量sleep(i);return 0;
}
线程分离
pthread_detach 函数
进程当中没有
实现线程分离
int pthread_detach(pthread_t thread);
成功:0;失败:错误号
- 线程分离状态:指定该状态,线程主动与主控线程断开关系。线程结束后,其退出状态不由其他线程获取,而 直接自己自动释放。网络、多线程服务器常用。
- 进程若有该机制,将不会产生僵尸进程。僵尸进程的产生主要由于进程死后,大部分资源被释放,一点残留资 源仍存于系统中,导致内核认为该进程仍存在。
也可使用 pthread_create 函数参 2(线程属性)来设置线程分离。
使用 pthread_detach 函数实现线程分离
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>void *tfn(void *arg)
{int n=3;while(n--){printf("thread count %d\n",n);sleep(1);} return (void *)1;
}int main(void)
{pthread_t tid;void *tret;int err;pthread_create(&tid,NULL,tfn,NULL);pthread_detach(tid); //让线程分离 ----自动退出,无系统残留资源while(1){err = pthread_join(tid,&tret);printf("---------err= %d\n",err);if(err != 0)fprintf(stderr,"thread error: %s\n",strerror(err)); elsefprintf(stderr,"thread exit code %d\n",(int)tret);sleep(1);} return 0;
}
注意事项
一般情况下,线程终止后,其终止状态一直保留到其它线程调用 pthread_join 获取它的状态为止。但是线程也 可以被置为 detach 状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。**不能对一个已 经处于 detach 状态的线程调用 pthread_join,这样的调用将返回 EINVAL 错误。**也就是说,如果已经对一个线程调用 了 pthread_detach 就不能再调用 pthread_join 了。
杀死线程
pthread_cancel 函数
杀死(取消)线程 其作用,对应进程中 kill() 函数。
int pthread_cancel(pthread_t thread);
成功:0;失败:错误号
注意:线程的取消并不是实时的,而有一定的延时。需要等待线程到达某个取消点(检查点)。
- 类似于玩游戏存档,必须到达指定的场所(存档点,如:客栈、仓库、城里等)才能存储进度。杀死线程也不是 立刻就能完成,必须要到达取消点。
- 取消点:是线程检查是否被取消,并按请求进行动作的一个位置。通常是一些系统调用
creat,open,pause, close,read,write.....
执行命令man 7 pthreads
可以查看具备这些取消点的系统调用列表。也可参阅APUE.12.7
取消选项小节。
可粗略认为一个系统调用(进入内核)即为一个取消点。如线程中没有取消点,可以通过调用 pthreestcancel 函数 自行设置一个取消点。 - 被取消的线程, 退出值定义在Linux的pthread库中。常数PTHREAD_CANCELED的值是**-1**。可在头文件pthread.h 中找到它的定义:
#define PTHREAD_CANCELED((void*)-1)
。因此当我们对一个已经被取消的线程使用pthread_join
回收时,得到的返回值为-1。
终止线程的三种方法
/*三种退出线程的方法*/ #include<stdio.h>
#include<string.h>
#include<pthread.h>void *tfn1(void *arg) //第一个线程
{printf("thread 1 returning\n");return (void *)111;
}void *tfn2(void *arg) //第二个线程
{printf("thread 2 exiting\n");pthread_exit((void *)222);
}void *tfn3(void *arg) //第三个线程
{while(1){printf("thread 3:I'm going to die in 3 seconds ....\n");sleep(1);//pthread_testcancel();//自己添加取消点}return (void *)666;
}int main(void)
{pthread_t tid;void *tret = NULL;pthread_create(&tid,NULL,tfn1,NULL);pthread_join(tid,&tret);printf("thread 1 exit code = %d\n\n",(int)tret);pthread_create(&tid,NULL,tfn2,NULL);pthread_join(tid,&tret);printf("thread 2 exit code = %d\n\n",(int)tret);pthread_create(&tid,NULL,tfn3,NULL);sleep(3);pthread_cancel(tid);pthread_join(tid,&tret);printf("thread 3 exit code = %d\n\n",(int)tret);return 0;
}
当把一个线程杀死后,它的返回值是-1;
终止线程方式
总结:终止某个线程而不终止整个进程,
有三种方法:
- 从线程主函数 return。这种方法对主控线程不适用,从 main 函数 return 相当于调用 exit。
- 一个线程可以调用 pthread_cancel 终止同一进程中的另一个线程。
- 线程可以调用 pthread_exit 终止自己。
pthread_equal 函数
比较两个线程 ID 是否相等。
int pthread_equal (pthread_t t1,pthread_t t2);
有可能 Linux 在未来线程 ID pthread_t
类型被修改为结构体实现。