在Python中处理阻塞函数调用

wis*_*shi 4 python multithreading gnuradio blocking

我正在使用Gnuradio框架.我处理我生成的流程图来发送/接收信号.这些流程图初始化并启动,但它们不会将控制流程返回给我的应用程序:

我进口了 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
Run Code Online (Sandbox Code Playgroud)

问题是:只有第一个if语句调用流图(与硬件交互).我陷入了困境.我可以使用一个Thread,但我没有经验,如何在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"
Run Code Online (Sandbox Code Playgroud)

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

谢谢,马吕斯

thi*_*red 6

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

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

2)玩信号可能会产生一些令人讨厌的非本地后果.例如,其他线程中的系统调用可能会被中断,这通常不是您想要的.通常,当收到(不是致命的)信号时,系统调用会重新启动.当你设置一个信号处理程序时,它会自动关闭整个进程或线程组的这种行为.检查'man siginterrupt'就可以了.

相信我 - 我之前遇到过两个问题而且根本不是很有趣.

在某些情况下,可以明确地避免阻塞 - 我强烈建议使用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)
Run Code Online (Sandbox Code Playgroud)

用以下方法测试:

mkfifo /tmp/fifo
python <code_above.py> /tmp/fifo (1st terminal)
echo abcd > /tmp/fifo (2nd terminal)
Run Code Online (Sandbox Code Playgroud)

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

当你无法明确强制非阻塞行为时,假设你只是使用外部库,那么它将会变得更加困难.线程可能会这样做,但显然它不是最先进的解决方案,通常是错误的.

我担心一般情况下你无法以强有力的方式解决这个问题 - 这实际上取决于你阻止的内容.