当前位置:  开发笔记 > 编程语言 > 正文

Python中的可中断线程连接

如何解决《Python中的可中断线程连接》经验,为你挑选了2个好方法。

有没有办法等待终止线程,但仍然拦截信号?

考虑以下C程序:

#include 
#include 
#include 
#include 
#include 
#include 

void* server_thread(void* dummy) {
    sleep(10);
    printf("Served\n");
    return NULL;
}

void* kill_thread(void* dummy) {
    sleep(1); // Let the main thread join
    printf("Killing\n");
    kill(getpid(), SIGUSR1);
    return NULL;
}

void handler(int signum) {
    printf("Handling %d\n", signum);
    exit(42);
}

int main() {
    pthread_t servth;
    pthread_t killth;

    signal(SIGUSR1, handler);

    pthread_create(&servth, NULL, server_thread, NULL);
    pthread_create(&killth, NULL, kill_thread, NULL);

    pthread_join(servth, NULL);

    printf("Main thread finished\n");
    return 0;
}

它在一秒后结束并打印:

Killing
Handling 10

相比之下,这是我尝试用Python编写的:

#!/usr/bin/env python
import signal, time, threading, os, sys

def handler(signum, frame):
    print("Handling " + str(signum) + ", frame:" + str(frame))
    exit(42)
signal.signal(signal.SIGUSR1, handler)

def server_thread():
    time.sleep(10)
    print("Served")
servth = threading.Thread(target=server_thread)
servth.start()

def kill_thread():
    time.sleep(1) # Let the main thread join
    print("Killing")
    os.kill(os.getpid(), signal.SIGUSR1)
killth = threading.Thread(target=kill_thread)
killth.start()

servth.join()

print("Main thread finished")

它打印:

Killing
Served
Handling 10, frame:

如何让它像C版本一样?



1> Jarret Hardi..:

在全局解释器锁定的情况下,Python中的线程有些奇怪.如果不依赖于加入超时,你可能无法达到你想要的效果而且正如eliben建议的那样.

文档中有两个点可以说明原因(也可能更多).

首先:

来自http://docs.python.org/library/signal.html#module-signal:

如果在同一程序中使用信号和线程,则必须小心.在同时使用信号和线程时要记住的基本要点是:始终在执行的主线程中执行signal()操作.任何线程都可以执行alarm(),getsignal(),pause(),setitimer()或getitimer(); 只有主线程可以设置一个新的信号处理程序,主线程将是唯一一个接收信号的线程(这是由Python信号模块强制执行的,即使底层线程实现支持向各个线程发送信号).这意味着信号不能用作线程间通信的手段.改用锁.

第二个来自http://docs.python.org/library/thread.html#module-thread:

线程与中断奇怪地交互:KeyboardInterrupt异常将由任意线程接收.(当信号模块可用时,中断始终转到主线程.)

编辑:这里有关于python bug跟踪器的机制的讨论:http://bugs.python.org/issue1167930.当然,最后Guido说:"这不太可能消失,所以你只需要忍受这个.正如你所发现的,指定一个超时解决了问题(某种程度)." YMMV :-)



2> phihag..:

Jarret Hardie已经提到了它:根据Guido van Rossum的说法,目前还没有更好的方法:如文档中所述,join(None)块(这意味着没有信号).替代方案 - 使用巨大超时(join(2**31)或左右)调用并检查isAlive看起来很棒.但是,Python处理计时器的方式是灾难性的,正如运行python测试程序servth.join(100)而不是servth.join():

select(0, NULL, NULL, NULL, {0, 1000})  = 0 (Timeout)
select(0, NULL, NULL, NULL, {0, 2000})  = 0 (Timeout)
select(0, NULL, NULL, NULL, {0, 4000})  = 0 (Timeout)
select(0, NULL, NULL, NULL, {0, 8000})  = 0 (Timeout)
select(0, NULL, NULL, NULL, {0, 16000}) = 0 (Timeout)
select(0, NULL, NULL, NULL, {0, 32000}) = 0 (Timeout)
select(0, NULL, NULL, NULL, {0, 50000}) = 0 (Timeout)
select(0, NULL, NULL, NULL, {0, 50000}) = 0 (Timeout)
select(0, NULL, NULL, NULL, {0, 50000}) = 0 (Timeout)
--- Skipped 15 equal lines ---
select(0, NULL, NULL, NULL, {0, 50000}Killing

即,Python每50毫秒唤醒一次,导致单个应用程序使CPU无法休眠.

推荐阅读
php
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有