Linux线程(三)终止线程与回收线程详解

1.终止线程

在示例代码,我们在新线程的启动函数(线程 start 函数)new_thread_start()通过 return 返回之后,意味着该线程已经终止了,除了在线程 start 函数中执行 return 语句终止线程外,终止线程的方式还有多种,可以通过如下方式终止线程的运行:

  • 线程的 start 函数执行 return 语句并返回指定值,返回值就是线程的退出码;
  • 线程调用 pthread_exit()函数;
  • 调用 pthread_cancel()取消线程(将在 6 小节介绍);

如果进程中的任意线程调用 exit()、_exit()或者_Exit(),那么将会导致整个进程终止,这里需要注意!

pthread_exit()函数将终止调用它的线程,其函数原型如下所示:

#include <pthread.h>

void pthread_exit(void *retval);

使用该函数需要包含头文件<pthread.h>。

参数 retval 的数据类型为 void *,指定了线程的返回值、也就是线程的退出码,该返回值可由另一个线程通过调用 pthread_join()来获取;同理,如果线程是在 start 函数中执行 return 语句终止,那么 return 的返回值也是可以通过 pthread_join()来获取的。参数 retval 所指向的内容不应分配于线程栈中,因为线程终止后,将无法确定线程栈的内容是否有效;

出于同样的理由,也不应在线程栈中分配线程 start 函数的返回值。

调用 pthread_exit()相当于在线程的 start 函数中执行 return 语句,不同之处在于,可在线程 start 函数所调用的任意函数中调用 pthread_exit()来终止线程。如果主线程调用了 pthread_exit(),那么主线程也会终止,但其它线程依然正常运行,直到进程中的所有线程终止才会使得进程终止。

使用示例

//示例代码 11.4.1 pthread_exit()终止线程使用示例

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>

static void *new_thread_start(void *arg) {
 printf("新线程 start\\n");
 sleep(1);
 printf("新线程 end\\n");
 pthread_exit(NULL);
}

int main(void) {
 pthread_t tid;
 int ret;
 ret = pthread_create(&tid, NULL, new_thread_start, NULL);
 if (ret) {
		 fprintf(stderr, "Error: %s\\n", strerror(ret));
		 exit(-1);
 }
 printf("主线程 end\\n");
 pthread_exit(NULL);
 exit(0);
}

新线程中调用 sleep()休眠,保证主线程先调用 pthread_exit()终止,休眠结束之后新线程也调用pthread_exit()终止,编译测试看看打印结果:

正如上面介绍到,主线程调用 pthread_exit()终止之后,整个进程并没有结束,而新线程还在继续运行。

2.回收线程

 在父、子进程当中,父进程可通过 wait()函数(或其变体 waitpid())阻塞等待子进程退出并获取其终止状态,回收子进程资源;而在线程当中,也需要如此,通过调用 pthread_join()函数来阻塞等待线程的终止,并获取线程的退出码,回收线程资源;pthread_join()函数原型如下所示:

#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);

使用该函数需要包含头文件<pthread.h>。

函数参数和返回值含义如下:

thread:

pthread_join()等待指定线程的终止,通过参数 thread(线程 ID)指定需要等待的线程;

retval:

如果参数 retval 不为 NULL,则 pthread_join()将目标线程的退出状态(即目标线程通过pthread_exit()退出时指定的返回值或者在线程 start 函数中执行 return 语句对应的返回值)复制到retval 所指向的内存区域;如果目标线程被 pthread_cancel()取消,则将 PTHREAD_CANCELED 放在retval 中。如果对目标线程的终止状态不感兴趣,则可将参数 retval 设置为 NULL。

返回值:

成功返回 0;失败将返回错误码。

调用 pthread_join()函数将会以阻塞的形式等待指定的线程终止,如果该线程已经终止,则 pthread_join()立刻返回。如果多个线程同时尝试调用 pthread_join()等待指定线程的终止,那么结果将是不确定的。

若线程并未分离(detached,将在 11.6.1 小节介绍),则必须使用 pthread_join()来等待线程终止,回收线程资源;如果线程终止后,其它线程没有调用 pthread_join()函数来回收该线程,那么该线程将变成僵尸线程,与僵尸进程的概念相类似;同样,僵尸线程除了浪费系统资源外,若僵尸线程积累过多,那么会导致应用程序无法创建新的线程。

当然,如果进程中存在着僵尸线程并未得到回收,当进程终止之后,进程会被其父进程回收,所以僵尸线程同样也会被回收。

所以,通过上面的介绍可知,pthread_join()执行的功能类似于针对进程的 waitpid()调用,不过二者之间存在一些显著差别:

  • 线程之间关系是对等的。进程中的任意线程均可调用 pthread_join()函数来等待另一个线程的终止。譬如,如果线程 A 创建了线程 B,线程 B 再创建线程 C,那么线程 A 可以调用 pthread_join()等待线程 C 的终止,线程 C 也可以调用 pthread_join()等待线程 A 的终止;这与进程间层次关系不同,父进程如果使用 fork()创建了子进程,那么它也是唯一能够对子进程调用 wait()的进程,线程之间不存在这样的关系。
  • 不能以非阻塞的方式调用 pthread_join()。对于进程,调用 waitpid()既可以实现阻塞方式等待、也可以实现非阻塞方式等待。

使用示例

//示例代码 11.5.1 pthread_join()等待线程终止
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>

static void *new_thread_start(void *arg) {
	 printf("新线程--running\\n");
	 for ( ; ; )
			 sleep(1);
	 return (void *)0; 
}

int main(void) {
	 pthread_t tid;
	 void *tret;
	 int ret;

	 /* 创建新线程 */
	 ret = pthread_create(&tid, NULL, new_thread_start, NULL);
	 if (ret) {
			 fprintf(stderr, "pthread_create error: %s\\n", strerror(ret));
			 exit(-1);
	 }
	 sleep(1);

	 /* 向新线程发送取消请求 */
	 ret = pthread_cancel(tid);
	 if (ret) {
			 fprintf(stderr, "pthread_cancel error: %s\\n", strerror(ret));
			 exit(-1);
	 }

	 /* 等待新线程终止 */
	 ret = pthread_join(tid, &tret);
	 if (ret) {
		 fprintf(stderr, "pthread_join error: %s\\n", strerror(ret));
		 exit(-1);
	 }

	 printf("新线程终止, code=%ld\\n", (long)tret);
	 exit(0);
}

主线程调用 pthread_create()创建新线程之后,新线程执行 new_thread_start()函数,而在主线程中调用pthread_join()阻塞等待新线程终止,新线程终止后,pthread_join()返回,将目标线程的退出码保存在*tret 所指向的内存中。

测试结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dola_Pan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值