12.7 取消一个线程

有时,我们想让一个线程可以要求另一个线程终止,就像给它发送一个信号一样。线程有方法可以做到这一点,与信号处理一样,线程可以在被要求终止时改变其行为。

先来看看用于请求一个线程终止的函数:

12.7 取消一个线程 - 图1

这个函数的定义简单易懂,提供一个线程标识符,我们就可以发送请求来取消它。但在接收到取消请求的一端,事情会稍微复杂一点,不过也不是非常复杂。线程可以用pthread_setcancelstate设置自己的取消状态。

12.7 取消一个线程 - 图2

第一个参数的取值可以是PTHREAD_CANCEL_ENABLE,这个值允许线程接收取消请求;或者是PTHREAD_CANCEL_DISABLE,它的作用是忽略取消请求。oldstate指针用于获取先前的取消状态。如果你对它没有兴趣,只需传递NULL给它。如果取消请求被接受了,线程就可以进入第二个控制层次,用pthread_setcanceltype设置取消类型。

12.7 取消一个线程 - 图3

type参数可以有两种取值:一个是PTHREAD_CANCEL_ASYNCHRONOUS,它将使得在接收到取消请求后立即采取行动;另一个是PTHREAD_CANCEL_DEFERRED,它将使得在接收到取消请求后,一直等待直到线程执行了下述函数之一后才采取行动。具体是函数pthread_join、pthread_cond_wait、pthread_cond_timedwait、pthread_testcancel、sem_wait或sigwait。

我们在本章中不会对它们全部进行介绍,因为并不是所有这些函数都会被经常用到。与往常一样,更详细的资料可以在它们的手册页中找到。

根据POSIX标准,其他可能阻塞的系统调用,如read、wait等也可以成为取消点。在撰写本书时,Linux还不支持所有这些系统调用都能成为取消点。但一些实验证明,某些阻塞调用,如sleep确实允许取消动作的发生。为安全起见,你可能会想在估计会被取消的代码中添加一些pthread_testcancel调用。

oldtype参数可以保存先前的状态,如果不想知道先前的状态,可以传递NULL给它。默认情况下,线程在启动时的取消状态为PTHREADCANCEL_ENABLE,取消类型是PTHREAD CANCEL_DEFERRED。

实 验 取一个线程

程序thread7.c还是基于thread1.c。这一次,主线程向它创建的线程发送一个取消请求。

12.7 取消一个线程 - 图4

12.7 取消一个线程 - 图5

运行这个程序时,我们将看到如下所示的输出结果,显示线程已被取消:

12.7 取消一个线程 - 图6

实验解析

以通常的方法创建了新线程后,主线程休眠一会儿(好让新线程有时间开始执行),然后发送一个取消请求。如下所示:

12.7 取消一个线程 - 图7

在新创建的线程中,我们首先将取消状态设置为允许取消,如下所示:

12.7 取消一个线程 - 图8

12.7 取消一个线程 - 图9

然后将取消类型设置为延迟取消,如下所示:

12.7 取消一个线程 - 图10

最后,线程在循环中等待被取消,如下所示:

12.7 取消一个线程 - 图11