完成当前迭代后,如何结束内部套接字操作的无限循环?
How can I end an infinite loop with socket operations inside after finishing current iteration?
我有一个无限循环,其中有一些操作在退出循环之前必须完全执行。也就是说,我正在使用套接字库连接到外部设备,我需要等待读取指令完成才能中断循环。
我尝试使用信号处理程序(如 )在检测到键盘中断时升起标志。
当前代码:
import videosensor
import signal
def signal_handler(signal, frame):
"""Raises a flag when a keyboard interrupt is raised."""
global interrupted
interrupted = True
if __name__ == '__main__':
camera = videosensor.VideoSensor(filename)
interrupted = False
signal.signal(signal.SIGINT, signal_handler)
while not interrupted:
location = camera.get_register()
#...
#More irrelevant stuff is executed.
#...
time.sleep(0.01)
#This code has to be executed after exiting while loop
camera_shutdown(camera)
在前面的代码中,videosensor.VideoSensor是一个class,包含从外部设备获取数据的套接字操作。主程序中使用的 get_register() 方法如下:
def get_register(self):
"""Read the content of the specified register.
"""
#Do some stuff
value = socket.recv(2048)
return value
问题:
我希望 while 循环持续执行,直到用户按下某个键或使用键盘中断,但在当前迭代完成之后。相反,使用以前的解决方案并不能按预期工作,因为它会中断正在进行的指令,并且如果它正在读取套接字,则会引发错误:
/home/.../client.pyc
in read_register(self, regkey)
164 reg = self._REGISTERS[regkey]
165 self.send('r,{}\n'.format(reg))
--> 166 value = socket.recv(2048)
167 #Convert the string input into a valid value e.g. list or int
168 formatted_result = ast.literal_eval(value)
error: [Errno 4] Interrupted system
编辑: 从下面的答案看来,似乎无法使用 Keyboard Interrupt 并避免套接字读取要中止的功能。尽管有捕获错误的解决方案,但它们无法避免读取取消。
不过,我有兴趣找到一种获取用户输入的方法,例如特定的按键按下,这会引发标志,将在循环结束时检查,在检查之前不会中断主例程的执行。
EDIT2: 使用的 OS 是 Linux 分布 Ubuntu 14.04
您创建了自定义信号处理程序,但没有覆盖默认的键盘中断行为。将 signal.signal(signal.SIGINT, signal_handler)
添加到您的代码中以完成此操作:
import videosensor
import signal
# Custom signal handler
def signal_handler(signal, frame):
"""Raises a flag when a keyboard interrupt is raised."""
global interrupted
interrupted = True
# Necessary to override default keyboard interrupt
signal.signal(signal.SIGINT, signal_handler)
if __name__ == '__main__':
# Main programme
快速搜索后我发现 this solution for your issue
基本上,您无能为力:当您向进程发送 SIGINT 时,套接字也会 return SIGINT。那么,您能做的最好的事情就是通过捕获套接字 EINTR
错误并继续循环来主动忽略该问题:
import errno
try:
# do something
value = conn.recv(2048)
except socket.error as (code, msg):
if code != errno.EINTR:
raise
避免 C-c
中断读取问题的替代解决方案是使用并行执行,在例程中读取套接字,并在另一个例程中处理用户输入:
import asyncio
async def camera_task(has_ended, filename):
camera = videosensor.VideoSensor(filename)
try:
while not has_ended.is_set():
location = camera.get_register()
#...
#More irrelevant stuff is executed.
#...
await asyncio.sleep(0.01)
finally:
#This code has to be executed after exiting while loop
camera_shutdown(camera)
async def input_task(shall_end):
while True:
i = input("Press 'q' to stop the script…")
if i == 'q':
shall_end.set()
def main():
filename = …
#
end_event = asyncio.Event()
asyncio.Task(camera_task(end_event, filename))
asyncio.Task(input_task(end_event))
asyncio.get_event_loop().run_forever()
import threading, time
def camera_task(has_ended, filename):
camera = videosensor.VideoSensor(filename)
try:
while not has_ended.is_set():
location = camera.get_register()
#...
#More irrelevant stuff is executed.
#...
time.sleep(0.01)
finally:
#This code has to be executed after exiting while loop
camera_shutdown(camera)
def input_task(shall_end):
while True:
i = input("Press 'q' to stop the script…")
if i == 'q':
shall_end.set()
def main():
filename = …
#
end_event = threading.Event()
threads = [
threading.Thread(target=camera_task, args=(end_event, filename)),
threading.Thread(target=input_task, args=(end_event,))
]
# start threads
for thread in threads:
thread.start()
# wait for them to end
for thread in threads:
thread.join()
import multiprocessing, time
def camera_task(has_ended, filename):
camera = videosensor.VideoSensor(filename)
try:
while not has_ended.is_set():
location = camera.get_register()
#...
#More irrelevant stuff is executed.
#...
time.sleep(0.01)
finally:
#This code has to be executed after exiting while loop
camera_shutdown(camera)
def input_task(shall_end):
while True:
i = input("Press 'q' to stop the script…")
if i == 'q':
shall_end.set()
def main():
filename = …
#
end_event = multiprocessing.Event()
processes = [
multiprocessing.Process(target=camera_task, args=(end_event, filename)),
multiprocessing.Process(target=input_task, args=(end_event,))
]
# start processes
for process in processes:
process.start()
# wait for them to end
for process in processes:
process.join()
disclaimer: those codes are untested, and there might be some typos or little errors, but I believe the overall logic should be
如果我理解正确,您不希望 socket.recv()
被打断,但您确实希望使用信号让用户指示 I/O 循环应该在当前 I/O 操作已完成。
假设你在Unix系统上使用Python2,你可以通过在进入循环前调用signal.siginterrupt(signal.SIGINT, False)
来解决你的问题。这将导致系统调用在信号发生时重新启动,而不是中断它并引发异常。
在您的情况下,这意味着 socket.recv()
操作将在您的信号处理程序被调用后重新启动,因此 get_register()
不会 return 直到在套接字上收到消息。如果这是您想要的,您的代码将是:
interrupted = False
old_handler = signal.signal(signal.SIGINT, signal_handler) # install signal handler
signal.siginterrupt(signal.SIGINT, False) # do not interrupt system calls
while not interrupted:
location = camera.get_register()
if location == '':
# remote connection closed
break
#...
#More irrelevant stuff is executed.
#...
time.sleep(0.01)
这是一种方法,但它确实要求您的代码在 Unix 平台上 运行。
另一种可能适用于其他平台的方法是处理异常,忽略进一步的 SIGINT
信号(以防用户再次点击中断),然后在之前执行最后的 socket.recv()
return来自 get_register()
函数:
import errno
def get_register(s):
"""Read the content of the specified register.
"""
#Do some stuff
try:
old_handler = None
return s.recv(2048)
except socket.error as exc:
if exc.errno == errno.EINTR:
old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) # ignore this signal
return s.recv(2048) # system call was interrupted, restart it
else:
raise
finally:
if old_handler is not None:
signal.signal(signal.SIGINT, old_handler) # restore handler
信号处理可能会很棘手,上面可能存在我不知道的竞争条件。如果可能,请尝试使用 siginterrupt()
。
我有一个无限循环,其中有一些操作在退出循环之前必须完全执行。也就是说,我正在使用套接字库连接到外部设备,我需要等待读取指令完成才能中断循环。
我尝试使用信号处理程序(如
当前代码:
import videosensor
import signal
def signal_handler(signal, frame):
"""Raises a flag when a keyboard interrupt is raised."""
global interrupted
interrupted = True
if __name__ == '__main__':
camera = videosensor.VideoSensor(filename)
interrupted = False
signal.signal(signal.SIGINT, signal_handler)
while not interrupted:
location = camera.get_register()
#...
#More irrelevant stuff is executed.
#...
time.sleep(0.01)
#This code has to be executed after exiting while loop
camera_shutdown(camera)
在前面的代码中,videosensor.VideoSensor是一个class,包含从外部设备获取数据的套接字操作。主程序中使用的 get_register() 方法如下:
def get_register(self):
"""Read the content of the specified register.
"""
#Do some stuff
value = socket.recv(2048)
return value
问题:
我希望 while 循环持续执行,直到用户按下某个键或使用键盘中断,但在当前迭代完成之后。相反,使用以前的解决方案并不能按预期工作,因为它会中断正在进行的指令,并且如果它正在读取套接字,则会引发错误:
/home/.../client.pyc in read_register(self, regkey)
164 reg = self._REGISTERS[regkey] 165 self.send('r,{}\n'.format(reg)) --> 166 value = socket.recv(2048) 167 #Convert the string input into a valid value e.g. list or int 168 formatted_result = ast.literal_eval(value)
error: [Errno 4] Interrupted system
编辑: 从下面的答案看来,似乎无法使用 Keyboard Interrupt 并避免套接字读取要中止的功能。尽管有捕获错误的解决方案,但它们无法避免读取取消。
不过,我有兴趣找到一种获取用户输入的方法,例如特定的按键按下,这会引发标志,将在循环结束时检查,在检查之前不会中断主例程的执行。
EDIT2: 使用的 OS 是 Linux 分布 Ubuntu 14.04
您创建了自定义信号处理程序,但没有覆盖默认的键盘中断行为。将 signal.signal(signal.SIGINT, signal_handler)
添加到您的代码中以完成此操作:
import videosensor
import signal
# Custom signal handler
def signal_handler(signal, frame):
"""Raises a flag when a keyboard interrupt is raised."""
global interrupted
interrupted = True
# Necessary to override default keyboard interrupt
signal.signal(signal.SIGINT, signal_handler)
if __name__ == '__main__':
# Main programme
快速搜索后我发现 this solution for your issue
基本上,您无能为力:当您向进程发送 SIGINT 时,套接字也会 return SIGINT。那么,您能做的最好的事情就是通过捕获套接字 EINTR
错误并继续循环来主动忽略该问题:
import errno
try:
# do something
value = conn.recv(2048)
except socket.error as (code, msg):
if code != errno.EINTR:
raise
避免 C-c
中断读取问题的替代解决方案是使用并行执行,在例程中读取套接字,并在另一个例程中处理用户输入:
import asyncio
async def camera_task(has_ended, filename):
camera = videosensor.VideoSensor(filename)
try:
while not has_ended.is_set():
location = camera.get_register()
#...
#More irrelevant stuff is executed.
#...
await asyncio.sleep(0.01)
finally:
#This code has to be executed after exiting while loop
camera_shutdown(camera)
async def input_task(shall_end):
while True:
i = input("Press 'q' to stop the script…")
if i == 'q':
shall_end.set()
def main():
filename = …
#
end_event = asyncio.Event()
asyncio.Task(camera_task(end_event, filename))
asyncio.Task(input_task(end_event))
asyncio.get_event_loop().run_forever()
import threading, time
def camera_task(has_ended, filename):
camera = videosensor.VideoSensor(filename)
try:
while not has_ended.is_set():
location = camera.get_register()
#...
#More irrelevant stuff is executed.
#...
time.sleep(0.01)
finally:
#This code has to be executed after exiting while loop
camera_shutdown(camera)
def input_task(shall_end):
while True:
i = input("Press 'q' to stop the script…")
if i == 'q':
shall_end.set()
def main():
filename = …
#
end_event = threading.Event()
threads = [
threading.Thread(target=camera_task, args=(end_event, filename)),
threading.Thread(target=input_task, args=(end_event,))
]
# start threads
for thread in threads:
thread.start()
# wait for them to end
for thread in threads:
thread.join()
import multiprocessing, time
def camera_task(has_ended, filename):
camera = videosensor.VideoSensor(filename)
try:
while not has_ended.is_set():
location = camera.get_register()
#...
#More irrelevant stuff is executed.
#...
time.sleep(0.01)
finally:
#This code has to be executed after exiting while loop
camera_shutdown(camera)
def input_task(shall_end):
while True:
i = input("Press 'q' to stop the script…")
if i == 'q':
shall_end.set()
def main():
filename = …
#
end_event = multiprocessing.Event()
processes = [
multiprocessing.Process(target=camera_task, args=(end_event, filename)),
multiprocessing.Process(target=input_task, args=(end_event,))
]
# start processes
for process in processes:
process.start()
# wait for them to end
for process in processes:
process.join()
disclaimer: those codes are untested, and there might be some typos or little errors, but I believe the overall logic should be
如果我理解正确,您不希望 socket.recv()
被打断,但您确实希望使用信号让用户指示 I/O 循环应该在当前 I/O 操作已完成。
假设你在Unix系统上使用Python2,你可以通过在进入循环前调用signal.siginterrupt(signal.SIGINT, False)
来解决你的问题。这将导致系统调用在信号发生时重新启动,而不是中断它并引发异常。
在您的情况下,这意味着 socket.recv()
操作将在您的信号处理程序被调用后重新启动,因此 get_register()
不会 return 直到在套接字上收到消息。如果这是您想要的,您的代码将是:
interrupted = False
old_handler = signal.signal(signal.SIGINT, signal_handler) # install signal handler
signal.siginterrupt(signal.SIGINT, False) # do not interrupt system calls
while not interrupted:
location = camera.get_register()
if location == '':
# remote connection closed
break
#...
#More irrelevant stuff is executed.
#...
time.sleep(0.01)
这是一种方法,但它确实要求您的代码在 Unix 平台上 运行。
另一种可能适用于其他平台的方法是处理异常,忽略进一步的 SIGINT
信号(以防用户再次点击中断),然后在之前执行最后的 socket.recv()
return来自 get_register()
函数:
import errno
def get_register(s):
"""Read the content of the specified register.
"""
#Do some stuff
try:
old_handler = None
return s.recv(2048)
except socket.error as exc:
if exc.errno == errno.EINTR:
old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) # ignore this signal
return s.recv(2048) # system call was interrupted, restart it
else:
raise
finally:
if old_handler is not None:
signal.signal(signal.SIGINT, old_handler) # restore handler
信号处理可能会很棘手,上面可能存在我不知道的竞争条件。如果可能,请尝试使用 siginterrupt()
。