You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()调用,程序依然会挂住,没有任何改善。

我的疑问

  1. 为什么Python中调用pthread_cancel后,线程不会在取消点响应,导致join()卡住?
  2. 替换成thread.native_id后出现段错误,这是哪里出了问题?
  3. 如果不能用pthread_cancel,那Python中终止阻塞线程的正确方式是什么?

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 10:50:30