背景
一个模块频繁启动退出会出现退出阻塞的问题!
因为有特殊业务用同事非阻塞库有问题,所以我暂时用线程阻塞加pthread_cancel强制退出;
肯定就是这个线程退出阻塞,加日志跟踪之。。
怀疑在pthread_join,测试发现阻塞在pthread_cancel!!
分析
基本概念
pthread_cancel调用并不等待线程终止,它只提出请求。线程在取消请求(pthread_cancel)发出后会继续运行,
直到到达某个取消点(CancellationPoint)。取消点是线程检查是否被取消并按照请求进行动作的一个位置.
测试伪代码
#include <unistd.h>
#include <sys/types.h>
#include<sys/socket.h>
#include<netdb.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<errno.h>
#include<malloc.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/ioctl.h>
#include<stdarg.h>
#include<fcntl.h>
static int socket_fd;
static void* pthread_func(void* arg)
{
char buf[128] = {0};
while(1){
printf("read start \r\n");
pthread_testcancel(); //阻塞时没有这行
read(socket_fd, buf, 128);
pthread_testcancel();//阻塞时没有这行
printf("read end\r\n");
}
return NULL;
}
int main(int argc, char const *argv[]) {
socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
pthread_t tid;
pthread_create(&tid, NULL, pthread_func, NULL);
sleep(2);
printf("pthread_cancel tid[%d]\r\n", tid);
int ret = pthread_cancel(tid);
printf("ret[%d]\r\n", ret);
pthread_join(tid, NULL);
printf("pthread_join\r\n");
close(socket_fd);
socket_fd = -1;
return 0;
}
非必现问题,必须实际环境测试, read要有接收
本地read无数据接收时无法复现问题!
原因
根据POSIX标准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait()、sigwait()等函数以及
read()、write()等会引起阻塞的系统调用都是Cancelation-point,而其他pthread函数都不会引起Cancelation动作。
但是pthread_cancel的手册页声称,由于LinuxThread库与C库结合得不好,因而目前C库函数都不是Cancelation-point;但CANCEL信号会使线程从阻塞的系统调用中退出,并置EINTR错误码,因此可以在需要作为Cancelation-point的系统调用前后调用pthread_testcancel(),从而达到POSIX标准所要求的目标.
即如下代码段:
pthread_testcancel();
retcode = read(fd, buffer, length);
pthread_testcancel();
注意:
程序设计方面的考虑,如果线程处于无限循环中,且循环体内没有执行至取消点的必然路径,则线程无法由外部其他线程的取消请求而终止。因此在这样的循环体的必经路径上应该加入pthread_testcancel()调用.
真相大白:
pthread_cancel的手册页声称:LinuxThread库与C库结合得不好,C库函数都不是Cancelation-point, 需要作Cancelation-point的系统调用前后调用pthread_testcancel(),从而达到POSIX标准所要求的目标
参考
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处!
更多精彩内容,欢迎访问一只海星的主页