我正在使用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语句调用流图(与硬件交互)。我陷入了困境。我可以使用一个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"
我如何获得print "hallo"
。 ;)
谢谢, 的Marius
答案 0 :(得分: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)
用以下方法测试:
mkfifo /tmp/fifo
python <code_above.py> /tmp/fifo (1st terminal)
echo abcd > /tmp/fifo (2nd terminal)
通过一些额外的努力,select()调用可以用作整个程序的主循环,聚合所有事件 - 你可以使用libev或libevent,或者围绕它们使用一些Python包装。
当你无法明确强制非阻塞行为时,假设你只是使用外部库,那么它将会变得更加困难。线程可能会这样做,但显然它不是最先进的解决方案,通常是错误的。
我担心一般情况下你无法以强有力的方式解决这个问题 - 这实际上取决于你阻止的内容。
答案 1 :(得分:4)
IIUC,每个top_block都有一个停止方法。所以你实际上可以在一个线程中运行top_block,并在超时到达时发出一个停止。如果top_block的wait()也有一个超时会更好,但唉,它没有。
在主线程中,您需要等待两种情况:a)top_block完成,b)超时到期。忙等待是邪恶的:-),所以你应该使用线程的join-with-timeout来等待线程。如果线程在连接后仍处于活动状态,则需要停止top_run。
答案 2 :(得分:2)
您可以设置信号警报,以便在超时时中断您的呼叫:
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"
事情是:
SIGALRM的默认处理程序是中止应用程序,如果您设置了处理程序,那么它应该不再停止该应用程序。
接收信号通常会中断系统调用(然后取消阻止您的应用程序)
答案 3 :(得分:2)
问题的简单部分与信号处理有关。从Python运行时的角度来看,在解释器进行系统调用时收到的信号作为OSError异常呈现给您的Python代码,其中errno
归因于errno.EINTR
所以这可能与你想要的大致相同:
#!/usr/bin/env python
import signal, os, errno, time
def handler(signum, frame):
# print 'Signal handler called with signal', signum
#raise IOError("Couldn't open device!")
print "timed out"
time.sleep(3)
def foo():
# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
try:
signal.alarm(3)
# This open() may hang indefinitely
fd = os.open('/dev/ttys0', os.O_RDWR)
except OSError, e:
if e.errno != errno.EINTR:
raise e
signal.alarm(0) # Disable the alarm
foo()
print "hallo"
注意我已经将time
的导入移出了函数定义,因为以这种方式隐藏导入似乎是不好的形式。我一点也不清楚为什么你在信号处理程序中睡觉,事实上,这似乎是一个相当糟糕的主意。
我想要做的关键点是任何(非忽略的)信号都会中断你的Python代码执行主线。您的处理程序将使用参数指示哪个信号编号触发执行(允许一个Python函数用于处理许多不同的信号)和一个框架对象(可用于某种调试或检测)。
由于代码的主要流程被中断,因此您必须将该代码包装在某些异常处理中,以便在发生此类事件后重新获得控制权。 (顺便提一下,如果你在C中编写代码,你就会有同样的问题;你必须为任何具有底层系统调用的库函数做好准备,以返回错误并在系统中处理-EINTR errno 通过循环返回重试或分支到主线中的某个替代(例如继续进行其他文件,或没有任何文件/输入等)。
正如其他人在回答您的问题时指出的那样,基于SIGALARM的方法很可能充满了可移植性和可靠性问题。更糟糕的是,其中一些问题可能是您在测试环境中永远不会遇到的竞争条件,并且可能仅在极难重现的条件下发生。丑陋的细节往往是在重新进入的情况下 - 如果在执行信号处理程序期间调度信号会发生什么?
我在一些脚本中使用了SIGALARM,在Linux下它对我来说不是问题。我正在处理的代码适合于该任务。它可能足以满足您的需求。
如果不了解Gnuradio代码的行为方式,从中实例化的对象类型以及返回的对象类型,您的主要问题很难回答。
浏览一下您链接的文档,我发现它们似乎没有提供任何可用于直接限制阻塞行为的“超时”参数或设置。在“控制流图”下的表中,我看到他们明确地说.run()
可以无限期地执行或者直到收到SIGINT。我还注意到.start()
可以启动应用程序中的线程,并且看起来会在运行时将控制权返回到Python代码行。 (这似乎取决于流程图的性质,我对此并不充分了解。)
听起来你可以创建流程图,.start()
,然后(在Python代码的主线上处理或睡眠一段时间后)调用控制对象上的.lock()
方法(结核病?)。我猜,这会将状态的Python表示...... Python对象......放入静止模式,以允许您查询状态,或者正如他们所说,重新配置流程图。如果您致电.run()
,则会在致电.wait()
后致电.start()
;并且.wait()
显然会一直运行,直到所有块“表明它们已完成”或直到您调用对象的.stop()
方法。
所以听起来你想使用.start()
而不是.run()
和.wait()
;然后在进行任何其他处理(包括.stop()
)后调用time.sleep()
。
也许就像这样简单:
tb = send_seq_2.top_block()
tb.start()
time.sleep(endtime - time.time())
tb.stop()
seq1_sent = True
tb = send_seq_2.top_block()
tb.start()
seq2_sent = True
..虽然我怀疑我的time.sleep()
。也许你想在查询tb
对象的状态时执行其他操作(可能需要在较小的时间间隔内休眠,调用其.lock()
方法,并访问我一无所知的属性,然后调用它{{1再次睡觉之前。
答案 4 :(得分:1)
if not seq1_sent:
tb = send_seq_2.top_block()
tb.Run(True)
seq1_sent = True
if time.time() < endtime:
break
如果'if time.time()&lt;结束时间:'那么你将突破循环,seq2_sent的东西永远不会被击中,也许你的意思是'time.time()&gt;在该测试中的“结束时间”?
答案 5 :(得分:0)
您可以尝试使用延迟执行...扭曲的框架使用它们很多
答案 6 :(得分:0)
你提到在Python中杀死线程 - 虽然只有在Python代码运行时才能杀死/中断另一个线程,而不是在C代码中,这是可能的,但这可能对你没有帮助。
看到另一个问题的答案: python: how to send packets in multi thread and then the thread kill itself
或google for killable python threads获取更多详细信息,如下所示: http://code.activestate.com/recipes/496960-thread2-killable-threads/
答案 7 :(得分:-1)
如果要在阻塞函数上设置超时,则将threading.Thread作为方法join(timeout)阻塞,直到超时为止。
基本上,类似的东西应该做你想要的:
import threading
my_thread = threading.Thread(target=send_seq_2.top_block)
my_thread.start()
my_thread.join(TIMEOUT)