用Go编写的Python扩展忽略Ctrl+C

pw136qt2  于 2023-06-21  发布在  Go
关注(0)|答案(1)|浏览(91)

我使用的是一个用go编写的python驱动程序,该驱动程序连接到某个服务并进行一些处理。我们发现服务的一个“unheanlthy”示例存在问题,导致驱动程序卡住,除非我们kill进程,否则无法终止。
在做一些实验时,我发现当执行用go编写的扩展时,程序会忽略发出的Ctrl+C命令,直到控制权回到Python,而C编写的扩展不会发生这种情况,在执行C代码时会引发KeyboardInterrupt。我的问题是为什么会发生这种情况,如果有办法解决这个问题或发出一种超时。尝试使用threading.Timer引发一个异常来模拟超时,但问题是该异常是在它自己的线程中抛出的,并且不会中断主线程。在Python(cpython)3.93.10中测试。至于我写的扩展,Go版本是1.20.4,C编译器是9.4.0
我会在下面留一个小房间。
Python代码:

import ctypes

import ctypes
go_library = ctypes.cdll.LoadLibrary('./go_library.so')
hello_Go = go_library.helloWorld

c_library = ctypes.cdll.LoadLibrary("./c_library.so")
hello_c = c_library.helloWorld

try:
    print("Calling golang code")
    hello_Go()

    print("Calling C code")
    hello_c()
except KeyboardInterrupt:
    print("Ctrl+C issued DD:")
finally:
    print("Done")

围棋推广

package main

import (
   "C"
   "log"
   "time"
)

func helloWorld(){
   log.Println("Hello World")
   time.Sleep(10 * time.Second)
   log.Println("Done sleeping")
}

func main(){
}

C扩展

#include <stdio.h>

int helloWorld() {
    printf("Hello from C\n");
    sleep(10);
    printf("Done sleeping from C\n");

    return 0;
}
zd287kbt

zd287kbt1#

我遇到了同样的情况。经过一番研究,我找到了解决办法。
根据this answer
Python在SIGINT上安装了一个信号处理程序,它只是设置一个由主解释器循环检查的标志。为了让这个处理程序正常工作,Python解释器必须运行Python代码。
看来在运行golang代码时,golang调用python安装了SIGINT信号处理程序来设置一个标志。因此,当运行回到python时,检测到KeyboardInterrupt。
来自Golang Doc:
(When调用Go代码的非Go程序)如果为异步信号调用Notify,则会为该信号安装Go信号处理程序。如果稍后为该信号调用Reset,则将重新安装该信号的原始处理,恢复非Go信号处理程序(如果有的话)。
因此,下面的代码将在go代码中捕获SIGINT,并在SIGINT时立即返回函数。

func helloWorld() {
    c := make(chan os.Signal)
    signal.Notify(c, syscall.SIGINT)

    // the original handling for that signal will be reinstalled, restoring the non-Go signal handler if any.
    defer signal.Reset(syscall.SIGINT)

    go func() {
        log.Println("Hello World")
        time.Sleep(10 * time.Second)
        log.Println("Done sleeping")
        c <- nil

    }()

    // wait go routine to finish or signal received
    <-c
}

相关问题