您的位置:首页 > 其它

99-再议 pthread_cancel

2017-03-17 14:01 393 查看
在线程创建与终止中就曾提到过 pthread_cancel 函数,它是用来终止指定的线程的,就好像线程自己调用了 pthread_exit(PTHREAD_CANCEL) 一样。不过,在那个时候并未讨论调用 pthread_cancel 后线程什么时候终止。

本文主要目的就是验证 pthread_cancel 的退出时机。

下面的几个程序,在线程中编写了计算密集型程序(一个很大的循环),循环执行完毕后,打印该循环消耗的时间。接下来,又打印了一行。

如果 cannot be canceled 打印出来,表明线程并未被取消。

1. 实验

1.1 代码

// cancel.c
#include <stdio.h>
#include <pthread.h>
#include <time.h>

void* fun(void *arg) {
int i, j, start;
start = time(0);
for (i = 0; i < 1000; ++i) {
for (j = 0; j < 1000000; ++j);
}
printf("finished, consume %ld s\n", time(0) - start);
printf("cannot be canceled\n");
return NULL;
}

int main() {
pthread_t tid;
pthread_create(&tid, NULL, fun, NULL);
pthread_cancel(tid);
puts("cancel thread");
pthread_join(tid, NULL);
return 0;
}


1.2 运行结果及分析



图1 实验 1 结果

从图 1 中可以看到,当主线程发出取消请求后,线程并未立即停止,而是一直等到打印了 finished, consume 4 s 才停止。

1.3 取消点

出现图 1 中的结果,原因在于 pthread_cancel 在默认情况下,只是通知线程,至于线程什么时候会取消,只有在遇到了取消点(把它想象成某个函数)的时候才会停止。在密集计算的循环中,并没有调用任何取消点函数。

当执行第一个 printf 函数时,线程退出了,说明在 printf 的调用链中,肯定有一个步骤调用了取消点函数。实际上,printf 在底层是调用了 write 接口,write 函数中存在取消点。

有很多函数都有取消点,具体可参考 apue 一书中的第 12 章——线程控制。

如果一直没有机会调用包含取消点的函数,那就意味着线程即使收到了“取消信号”也不会退出。这时候可以显示的在线程内部调用
void pthread_testcancel(void)
进行测试,如果真的收到了“取消信号”,则退出。

2. 默认取消和异步取消

有一种办法,可以让线程还没到达取消点的时候直接退出。这时候需要将线程设置为异步取消的方式。具体方法为在线程内部调用下面的函数:

int pthread_setcanceltype(int type, int *oldtype);


其中,type 的值如下:

PTHREAD_CANCEL_ASYNCHRONOUS


PTHREAD_CANCEL_DEFERRED


默认情况下,线程取消方式为默认值——
PTHREAD_CANCEL_DEFERRED
。要想让线程收到“取消信号”后立即退出,需要将 type 设置为
PTHREAD_CANCEL_ASYNCHRONOUS
.

2.1 代码

// asyncancel.c
#include <stdio.h>
#include <pthread.h>
#include <time.h>

void* fun(void *arg) {
int i, j, start, oldtype;
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
start = time(0);
for (i = 0; i < 1000; ++i) {
for (j = 0; j < 1000000; ++j);
}
printf("finished, consume %ld s\n", time(0) - start);
printf("cannot be canceled\n");
return NULL;
}

int main() {
pthread_t tid;
pthread_create(&tid, NULL, fun, NULL);
sleep(1);
pthread_cancel(tid);
puts("cancel thread");
pthread_join(tid, NULL);
return 0;
}


2.2 运行结果



图2 异步取消

从图 2 可以看到,printf 函数还未执行,或者说还没遇到取消点,线程就退出了。

3. 禁止取消

有些线程不是你想取消就取消的。就算你发送了 pthread_cancel 通知了,它也不会退出。除非它自己想退出了。

这时候,可以在线程函数中禁止取消。下面的函数可以实现此功能:

int pthread_setcancelstate(int state, int *oldstate);


其中,state 可以为下面的值:

PTHREAD_CANCEL_ENABLE


PTHREAD_CANCEL_DISABLE


默认情况下,state 的值为
PTHREAD_CANCEL_ENABLE
,即可以被取消。如果将 state 设置为
PTHREAD_CANCEL_DISABLE
,则线程是无法被取消的。

3.1 代码

// disablecancel.c
#include <stdio.h>
#include <pthread.h>
#include <time.h>

void* fun(void *arg) {
int i, j, start, oldstate;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
start = time(0);
for (i = 0; i < 1000; ++i) {
for (j = 0; j < 1000000; ++j);
}
printf("finished, consume %ld s\n", time(0) - start);
printf("cannot be canceled\n");
return NULL;
}

int main() {
pthread_t tid;
pthread_create(&tid, NULL, fun, NULL);
pthread_cancel(tid);
puts("cancel thread");
pthread_join(tid, NULL);
return 0;
}


3.2 运行结果



图3 将线程设置为禁用取消状态

从图 3 中可以看到,将线程设置为
PTHREAD_CANCEL_DISABLE
后,线程是无法被取消的。

4. 总结

理解取消点的概念

掌握异步取消

掌握禁用取消
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息