前几天对架构动了个小手术,居然因为一个线程的原因,在某个特殊情况下导致了core dump。虽然问题很快定位并解决,但还是决定把线程这块东西再捋一捋。今天就说下几种线程的终止方式。
说终止线程之前,顺道简单提一下地球程序员都知道的创建线程,这里只介绍Linux哈。
在Linux下,可通过pthread_create()创建一条新线程:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
其中,pthread_t*类型的参数thread指向pthrad_t类型的缓冲区,在pthread_create()返回前,会在此保存一个该线程的唯一标识。
后续的Pthreads函数将使用该标识来引用此线程。
参数attr是一个指向pthread_attr_t类型对象的指针,该对象指定了新线程的各种属性,在实际应用中,多使用默认属性,即将该值设置为NULL。
start_routine()就是要执行的线程函数主体,arg是它的参数。arg是void*类型,意味着可以将任意对象的指针传递给start_routine函数。如果需要向start_routine传递多个参数,可将arg指向一个结构体,结构体中的各个字段对应于待传递的参数。
好了,线程创建好之后,怎么终止它呢?这里不考虑异常退出的情况,终止线程有如下几种方式:
1. 通过return的方式退出线程函数;
2. 线程调用pthread_exit()退出线程;
3. 调用pthread_cancel()取消线程;
4. 任意线程调用exit(),或main()函数执行return语句,会导致进程立即终止,当然,进程中的所有线程也将立即终止。
第1种方式就是函数的return,再简单不过,此处不赘述。
第2种方式跟第1种方式类似,也是线程内部退出的方式:pthread_exit(void* retval), retval指定了线程的返回值。pthread_exit()相当于在线程函数start_routine()中执行了return,不同的是,可在start_routine()函数所调用的任意函数中调用pthread_exit()。
第3种方式是从外部终止的方式,pthread_cancel()作用是向指定的线程发送一个取消请求。函数原型如下:
#include <pthread.h>
int pthread_cancel(pthread_t thread); //Return 0 on success, or a positive error number on error
发出请求后,函数pthread_cancel()当即返回,不会等待目标线程的退出。一个简单的pthread_cancel的例子:
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
static void* testFunc(void *arg)
{
int i;
printf("New thread started. \n");
for(i = 0; ; i++)
{
printf("Loop %d\n", i);
sleep(1);
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t thread_test;
int s;
void *res = NULL;
s = pthread_create(&thread_test, NULL, testFunc, NULL);
if(s != 0)
{
printf("pthread_create error! \n");
return -1;
}
sleep(3);
s = pthread_cancel(thread_test);
if(s != 0)
{
printf("pthread_cancel error! \n");
return -1;
}
s = pthread_join(thread_test, &res);
if(s != 0)
{
printf("pthead_join error! \n");
return -1;
}
if(res == PTHREAD_CANCELED)
{
printf("Thread is canceled. \n");
}
else
{
printf("Thread is failed to be canceled. \n");
}
return 0;
}
执行结果:
可见,线程的状态在pthread_join()中可以得到,上例中是保存在了第二个参数res里面。当然,thread_cancel还会涉及到取消点、资源清理等很多内容,时间关系,本篇先不展开。
最后提一下第4种线程终止方式,即在任何一个线程中调用exit(),导致整个进程终止,进程都退出了,线程自然也就终止了。这种也算是从外部终止线程的方式了。