嵌入式Linux系统编程学习之二十八线程的等待退出


一、等待线程退出

  线程从入口点函数自然返回,或者主动调用 pthread_exit 函数都可以让线程正常终止。线程从入口点函数自然返回时,函数返回值可以被其他线程用 pthread_join 函数获取。
  pthread_join 函数的原型为:

	#include <pthread.h>
	int pthread_join(pthread_t pthid, void * *thread_return);

  pthread_join 函数是一个阻塞函数,一直等到参数 pthid 指定的线程返回,与多进程中的 wait 或 waitpid 类似。
  thread_return 是一个传出参数,接收线程函数的返回值。如果线程通过调用函数 pthread_exit 终止,则函数 pthread_exit 中的参数相当于自然返回值,照样可以被其他线程用函数 pthread_join 获取到。

  pthraed_join 函数还有一个非常重要的作用,由于一个进程中的多个线程共享数据段,因此通常在一个线程退出后,退出线程所占用的资源并不会随线程结束而释放。如果 th 线程类型并不是自动清理资源类型的,则 th 线程退出后,线程本身的资源必须通过其他线程调用 pthread_join 来清除,这相当于多进程程序中的 waitpid。

二、线程的取消

  一个线程也可以被其他线程杀掉,在 Linux 中的说法是一个线程被另一个线程取消 (cancel) 。
  线程取消的方法是一个线程向目标线程发 cancel 信号,但是如何处理 cancel 信号则由目标线程自己决定,目标线程或者忽略,或者立即终止,或者继续运行至 cancelation-point (取消点) 后终止。
  根据 POSIX 标准,pthread_join、pthread_testcancel、pthread_cond_wait、pthread_cond_timedwait、sem_wait、sigwait 等函数及 read、write 等会引起阻塞的系统调用都是 Cancelation-point ,而其他 pthread 函数都不会引起 Cancelation 动作。但是 pthread_cancel 的手册页声称,由于 Linux 线程库与 C 库结合的不好,因而目前 C 库函数都不是 Cancelation-point ;但 CANCEL 信号会使线程从阻塞的系统调用中退出,并置 EINTR 错误码,因此可以在需要作为 Cancelation-point 的系统调用前后调用 pthread_testcancel 函数,从而达到 POSIX 标准所要求的目标,即如下代码段:

	pthread_testcancel();
	retcode = read(fd, buffer, length);
	pthread_testcancel();

  但是从 RedHat9.0 的实际测试看,至少有些 C 库函数的阻塞函数是取消点,如 read、getchar 等,而 sleep 函数不管线程是否设置了 pthread_setcancelstat (PTHREAD_CANCEL_DISABLE, NULL),都起到取消点作用。总之,线程的取消一方面是一个线程强行杀死另一个线程,从程序设计角度看并不是一种好的风格,另一方面目前 Linux 本身对这方面的支持并不完善,所以在实际应用中应该谨慎使用。

	int pthread_cancel(pthread_t thread); //尽量不要用,linux支持并不完善

三、线程终止清理函数

  不论是可预见的线程终止还是异常终止,都会存在资源释放问题,在不考虑因运行出错而退出的前提下,如何保证线程终止时能顺利地释放掉自己所占用的资源,特别是锁资源,是一个必须考虑解决的问题。
  经常出现的情形是资源独占锁的使用:线程为了访问临界共享资源而为其加上锁,但在访问过程中该线程被外界取消,或者发生了中断,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可预见的,因此的确需要一个机制来简化用于资源释放的编程。
  在 POSIX 线程 API 中提供了一个 pthread_cleanup_push / pthread_cleanup_pop 函数对用于自动释放资源:从 pthread_cleanup_push 的调用点到 pthread_cleanup_pop 之间的程序段中的终止动作都将执行 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 函数带有一个 “}” ,因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。

  pthread_cleanup_pop 的参数 execute 如果为非 0 值,则按栈的顺序注销掉一个原来注册的清理函数时会执行该函数;当 pthread_cleanup_pop 的参数为 0 时,仅在线程调用 pthread_exit 函数或者其他线程对本线程调用 pthread_cancel 函数时,才弹出 “清理函数” 的同时执行该 “清理函数”。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值