Python中使用pthread_cancel处理阻塞线程异常:join卡住及native_id导致段错误的问题咨询
Python中使用pthread_cancel处理阻塞线程异常:join卡住及native_id导致段错误的问题咨询
我最近在尝试用C标准库的pthread_cancel()函数终止Python中的阻塞线程,过程中遇到了两个棘手的问题,想请教下各位开发者:
问题场景与测试代码
Python端测试代码
我写了如下Python代码,通过ctypes调用libc的pthread相关函数,尝试终止一个在sleep()中阻塞的线程:
import threading import ctypes from time import sleep import os import sys libc = ctypes.CDLL("libc.so.6") pthread_t = ctypes.c_ulong PTHREAD_CANCEL_DEFERRED = 0 PTHREAD_CANCEL_ASYNCHRONOUS = 1 canceltype = ctypes.c_int pthread_setcanceltype = libc.pthread_setcanceltype pthread_setcanceltype.argtypes = [ctypes.c_int, ctypes.POINTER(canceltype)] pthread_setcanceltype.restype = ctypes.c_int PTHREAD_CANCEL_ENABLE = 0 PTHREAD_CANCEL_DISABLE = 1 cancelstate = ctypes.c_int pthread_setcancelstate = libc.pthread_setcancelstate pthread_setcancelstate.argtypes = [ctypes.c_int, ctypes.POINTER(cancelstate)] pthread_setcancelstate.restype = ctypes.c_int pthread_cancel = libc.pthread_cancel pthread_cancel.argtypes = [pthread_t] pthread_cancel.restype = ctypes.c_int def terminate_thread(thread): if not thread.is_alive(): return res = pthread_cancel(thread.ident) if res != 0: raise SystemError("Error while stopping thread:", res) def thread2(): prev_type = canceltype() ret = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, ctypes.byref(prev_type)) if ret != 0: err = ctypes.get_errno() print(f"pthread_setcanceltype failed: errno={err}") return print('prev_type=', prev_type) prev_state = cancelstate() ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, ctypes.byref(prev_state)) if ret != 0: err = ctypes.get_errno() print(f"pthread_setcancelstate failed: errno={err}") return print('prev_state=', prev_state) print("thread2 start reading") sleep(1000) # sleep是标准取消点 print("thread2 ends") thr2 = threading.Thread(target=thread2) thr2.start() sleep(2) print("killing thr2") terminate_thread(thr2) thr2.join() print("main exit")
Python代码运行现象
运行后程序输出如下,之后就卡在thr2.join()处,线程并未被成功取消:
prev_type= c_int(0) prev_state= c_int(0) thread2 start reading killing thr2
对比的C语言代码
为了验证逻辑正确性,我写了对应的C语言版本,逻辑完全一致:
#include <stdio.h> #include <pthread.h> #include <unistd.h> void * thread2(void *){ printf("thread2 start reading\n"); getchar(); // getchar是标准取消点 printf("thread2 ends\n"); } int main(){ printf("PTHREAD_CANCEL_DEFERRED = %d\nPTHREAD_CANCEL_ASYNCHRONOUS = %d\nPTHREAD_CANCEL_ENABLE = %d\nPTHREAD_CANCEL_DISABLE = %d\n", PTHREAD_CANCEL_DEFERRED,PTHREAD_CANCEL_ASYNCHRONOUS,PTHREAD_CANCEL_ENABLE,PTHREAD_CANCEL_DISABLE); pthread_t thr2; pthread_create(&thr2, NULL, thread2, NULL); sleep(2); printf("killing thr2\n"); pthread_cancel(thr2); pthread_join(thr2, NULL); printf("main exit\n"); }
C代码运行现象
C版本运行完全正常,线程被成功取消,程序顺利退出,输出如下:
PTHREAD_CANCEL_DEFERRED = 0 PTHREAD_CANCEL_ASYNCHRONOUS = 1 PTHREAD_CANCEL_ENABLE = 0 PTHREAD_CANCEL_DISABLE = 1 thread2 start reading killing thr2 main exit
我排查到的问题点
后来我了解到,Python的thread.ident是Python解释器层面的线程ID,并不是底层Linux的pthread_t,应该用thread.native_id来获取真实的pthread线程ID。但当我把terminate_thread里的thread.ident替换成thread.native_id后,程序直接触发了段错误。
另外我也尝试过把线程的取消类型改成PTHREAD_CANCEL_ASYNCHRONOUS,并且去掉join()调用,程序依然会挂住,没有任何改善。
我的疑问
- 为什么Python中调用
pthread_cancel后,线程不会在取消点响应,导致join()卡住? - 替换成
thread.native_id后出现段错误,这是哪里出了问题? - 如果不能用
pthread_cancel,那Python中终止阻塞线程的正确方式是什么?
内容来源于stack exchange
相关产品推荐
相关产品推荐

