在Python中处理阻塞函数调用

2024-03-28 10:26:42 发布

您现在位置:Python中文网/ 问答频道 /正文

我在和Gnuradio framework一起工作。我处理生成的用于发送/接收信号的流程图。这些流图初始化并启动,但不会将控制流返回到我的应用程序:

我导入了time

while time.time() < endtime:
        # invoke GRC flowgraph for 1st sequence
        if not seq1_sent:
            tb = send_seq_2.top_block()
            tb.Run(True)
            seq1_sent = True
            if time.time() < endtime:
                break

        # invoke GRC flowgraph for 2nd sequence
        if not seq2_sent:
            tb = send_seq_2.top_block()
            tb.Run(True)
            seq2_sent = True
            if time.time() < endtime:
                break

问题是:只有第一个if语句调用流图(与硬件交互)。我被困在这里面了。我可以使用一个线程,但我没有经验如何在Python中超时线程。我怀疑这是可能的,因为似乎杀死线程不在api中。这个脚本只需要在Linux上运行。。。

如何正确地使用Python处理阻塞函数-而不杀死整个程序。 另一个更具体的例子是:

import signal, os

def handler(signum, frame):
        # print 'Signal handler called with signal', signum
        #raise IOError("Couldn't open device!")
        import time
        print "wait"
        time.sleep(3)


def foo():
    # Set the signal handler and a 5-second alarm
    signal.signal(signal.SIGALRM, handler)
    signal.alarm(3)

    # This open() may hang indefinitely
    fd = os.open('/dev/ttys0', os.O_RDWR)
    signal.alarm(0)          # Disable the alarm


foo()
print "hallo"

我怎么还能得到print "hallo"。;)

谢谢, 马吕斯


Tags: truesignaliftimeosopen线程tb
3条回答

首先,应不惜一切代价避免使用信号:

1)可能导致僵局。SIGALRM可能在阻塞系统调用之前到达进程(想象一下系统中的超高负载!)系统调用不会被中断。僵局。

2)玩弄信号可能会产生一些恶劣的非本地后果。例如,其他线程中的系统调用可能会被中断,这通常不是您想要的。通常系统调用在收到(不是致命的)信号时重新启动。设置信号处理程序时,它会自动关闭整个进程或线程组的此行为。看看上面的“男人信号中断”。

相信我-我以前遇到过两个问题,它们一点都不好玩。

在某些情况下,可以明确地避免阻塞——我强烈建议使用select()和friends(在Python中选中select模块)来处理阻塞的写入和读取。但这无法解决阻塞open()调用。

为此,我已经测试了这个解决方案,它对命名管道很有效。它以非阻塞方式打开,然后将其关闭,如果没有可用的内容,则使用select()调用最终超时。

import sys, os, select, fcntl

f = os.open(sys.argv[1], os.O_RDONLY | os.O_NONBLOCK)

flags = fcntl.fcntl(f, fcntl.F_GETFL, 0)
fcntl.fcntl(f, fcntl.F_SETFL, flags & ~os.O_NONBLOCK)

r, w, e = select.select([f], [], [], 2.0)

if r == [f]:
    print 'ready'
    print os.read(f, 100)
else:
    print 'unready'

os.close(f)

用以下方法测试:

mkfifo /tmp/fifo
python <code_above.py> /tmp/fifo (1st terminal)
echo abcd > /tmp/fifo (2nd terminal)

通过一些额外的努力,select()调用可以用作整个程序的主循环,聚合所有事件-您可以使用libev或libevent,或者使用一些Python包装器来包装它们。

当你不能明确地强制非阻塞行为时,比如说你只是使用一个外部库,那么它将变得更加困难。线程可以做到,但显然它不是一个最先进的解决方案,通常是错误的。

恐怕一般来说,你不能用一种强有力的方法来解决这个问题——这真的取决于你阻止了什么。

IIUC,每个顶层块都有一个停止方法。因此,您实际上可以在线程中运行top_块,并在超时到达时发出停止。如果top_块的wait()也有超时,那会更好,但唉,它没有

在主线程中,您需要等待两种情况:a)top_块完成,b)超时过期。繁忙的等待是邪恶的:-),因此您应该使用线程的join with timeout来等待线程。如果连接后线程仍处于活动状态,则需要停止顶部运行。

您可以设置一个信号警报,它会在超时时中断您的通话:

http://docs.python.org/library/signal.html

signal.alarm(1) # 1 second

my_blocking_call()
signal.alarm(0)

如果要确保不会破坏应用程序,也可以设置信号处理程序:

def my_handler(signum, frame):
    pass

signal.signal(signal.SIGALRM, my_handler)

编辑: 这段代码怎么了?这不应中止您的应用程序:

import signal, time

def handler(signum, frame):
    print "Timed-out"

def foo():
    # Set the signal handler and a 5-second alarm
    signal.signal(signal.SIGALRM, handler)
    signal.alarm(3)

    # This open() may hang indefinitely
    time.sleep(5)
    signal.alarm(0)          # Disable the alarm


foo()
print "hallo"

问题是:

  1. SIGALRM的默认处理程序是中止应用程序,如果设置了处理程序,则它将不再停止应用程序。

  2. 接收信号通常会中断系统调用(然后取消阻止应用程序)

相关问题 更多 >