hsf*_*xjy 16 python signals reentrancy race-condition python-3.x
码:
# callee.py
import signal
import sys
import time
def int_handler(*args):
for i in range(10):
print('INTERRUPT', args)
sys.exit()
if __name__ == '__main__':
signal.signal(signal.SIGINT, int_handler)
signal.signal(signal.SIGTERM, int_handler)
while 1:
time.sleep(1)
Run Code Online (Sandbox Code Playgroud)
# caller.py
import subprocess
import sys
def wait_and_communicate(p):
out, err = p.communicate(timeout=1)
print('========out==========')
print(out.decode() if out else '')
print('========err==========')
print(err.decode() if err else '')
print('=====================')
if __name__ == '__main__':
p = subprocess.Popen(
['/usr/local/bin/python3', 'callee.py'],
stdout=sys.stdout,
stderr=subprocess.PIPE,
)
while 1:
try:
wait_and_communicate(p)
except KeyboardInterrupt:
p.terminate()
wait_and_communicate(p)
break
except subprocess.TimeoutExpired:
continue
Run Code Online (Sandbox Code Playgroud)
只需执行caller.py
然后按Ctrl+C
,程序将RuntimeError: reentrant call inside <_io.BufferedWriter name='<stdout>'>
随机提升.从文档中我了解到信号处理程序是异步调用的,在这种情况下,几乎同时发送两个信号SIGINT(Ctrl+C
action)和SIGTERM(p.terminate()
),从而导致竞争条件.
但是,从这篇文章中我了解到该signal
模块不会在低级(C)处理程序中执行信号处理程序.相反,它设置一个标志,解释器检查字节码指令之间的标志,然后调用python信号处理程序.换句话说,虽然信号处理程序可能会破坏主线程中的控制流,但字节码指令总是原子的.
这似乎与我的示例程序的结果相矛盾.就我而言,print
隐式_io.BufferedWriter
都是用纯C实现的,因此调用print
函数应该只消耗一个字节码指令(CALL_FUNCTION
).我很困惑:在一个线程上的一个不间断的指令中,一个函数如何可以重入?
我正在使用Python 3.6.2.
您可能更愿意禁止向子进程传递 SIGINT,这样就不会出现竞争,也许可以将其放入不同的进程组中,或者让它忽略信号。那么只有来自父级的 SIGTERM 才重要。
要显示中断的位置,请使用以下命令:
sig_num, frame = args
print(dis.dis(frame.f_code.co_code))
print(frame.f_lasti)
Run Code Online (Sandbox Code Playgroud)
左边距中的字节码偏移量对应于最后执行的指令的偏移量。
其他感兴趣的项目包括
frame.f_lineno
、
frame.f_code.co_filename
和
frame.f_code.co_names
。
这个问题在 python 3.7.3 中变得毫无意义,不再表现出该症状。