问题描述
我有一个Python脚本(在2.7上运行),当我从命令行相对于后台运行该脚本时,其行为有所不同.当我从终端运行它时,它按预期运行,这两个线程作为守护程序运行,将输出写入窗口,而主循环则等待quit命令.它会一直运行,直到我输入quit:
I have a Python script (run on 2.7) that behaves differently when I run it in from the command line versus the background. When I run it from the terminal it runs as expected, the two threads run as daemons writing output to the window while the main loop waits for the quit command. It runs forever until I enter quit:
python test.py
当同一程序在后台运行时,两个线程都运行一次,然后程序挂起(我将其范围缩小到raw_input,我想我做出了一个错误的假设,即使两个线程将继续运行,即使在后台运行,而raw_input阻止了主线程(例如,由于在这种情况下没有输入,因此这两个线程基本上将永远运行).
When the same program is run in the background, both threads run one time and then the program hangs (I've narrowed it down to the raw_input, I guess I made a incorrect assumption that the two threads would continue on even if run in the background and raw_input blocked the main one. e.g the two threads would basically run forever since there is no input in this scenario).
python test.py &
我的目标是让一个程序运行这些循环(可能永远),但是如果我从终端运行该程序,它将接受输入.
My goal is to have one program with those loops running (potentially forever) but if I ran it from the terminal would accept input.
为了允许程序从终端/在后台运行,我是否需要在raw_input前面放置一个if语句,以检查该程序是否在后台,或者我是否缺少其他有帮助的东西? /p>
In order to allow the program to run both from the terminal / in the background do I need to basically put an if statement before the raw_input that checks whether it's in the background or not or am I missing else that would help?
import sys
import time
from threading import Thread
def threadOne():
while True:
print("Thread 1")
time.sleep(1)
def threadTwo():
while True:
print("Thread 2")
time.sleep(1)
# Run the threads in the background as daemons
threadOne = Thread(target = threadOne)
threadOne.daemon = True
threadOne.start()
threadTwo = Thread(target = threadTwo)
threadTwo.daemon = True
threadTwo.start()
# Main input loop. This will allow us to enter input. The
# threads will run forever unless "quit" is entered. This
# doesn't run when the program is run in the background (I
# incorrectly assumed it would just run forever with no input
# ever being entered in that scenario).
while True:
userInput = ""
userInput = raw_input("")
time.sleep(1)
# This should allow us to exit out
if str(userInput) == "quit":
sys.exit()
推荐答案
以某种可能的方式 起作用(我假设您正在* nix上运行它),但是如果用户要将进程发送回后台(例如,使用 ,然后在raw_input
等待用户输入时使用%&
在后台将其恢复.然后,stdin
上的读取将被阻止,就像在后台一样,从而导致内核停止进程,因为这是stdio的工作方式.如果可以接受(基本上用户必须在暂停过程之前按Enter键),则只需执行以下操作:
In a way this probably works (I am assuming you are running this on a *nix), however if user were to send the process backing into background (i.e. suspend it using then resuming it in background with %&
) while raw_input
is waiting on user input, then the read on stdin
will then be blocked as it is in the background, thus causing the kernel to stop the process as this is how stdio works. If this is acceptable (basically user has to hit enter before suspending the process), you can simply do this:
import os
while True:
userInput = ""
if os.getpgrp() == os.tcgetpgrp(sys.stdout.fileno()):
userInput = raw_input("")
time.sleep(1)
os.getpgrp
的作用是返回当前os组的ID,然后 os.tcgetpgrp
获取与此进程的stdout关联的进程组,如果它们匹配,则表示此进程当前在前台,这意味着您可以调用raw_input
而不阻塞线程.
另一个问题提出了类似的问题,我在以下地方有更长的解释:.
Another question raised a similar issue and I have a longer explanation at: Freeze stdin when in the background, unfreeze it when in the foreground.
更好的方法是将其与 select.poll
,并从标准I/O单独寻址交互式I/O(直接使用/dev/tty
),因为您不希望stdin/stdout重定向被污染".这是同时包含这两个想法的更完整的版本:
tty_in = open('/dev/tty', 'r')
tty_out = open('/dev/tty', 'w')
fn = tty_in.fileno()
poll = select.poll()
poll.register(fn, select.POLLIN)
while True:
if os.getpgrp() == os.tcgetpgrp(fn) and poll.poll(10): # 10 ms
# poll should only return if the input buffer is filled,
# which is triggered when a user enters a complete line,
# which lets the following readline call to not block on
# a lack of input.
userInput = tty_in.readline()
# This should allow us to exit out
if userInput.strip() == "quit":
sys.exit()
仍需要进行背景/前景检测,因为该过程尚未完全从外壳分离(因为可以将其带回前台),因此如果发送任何输入,poll
将返回tty的fileno
进入外壳程序,如果这触发了readline,则会停止该过程.
$ python script.py < script.py 2> stderr
input stream length: 2116
在下面的完整示例中,它还向用户提供提示,即,每当发送命令或将过程返回到前台时,都会显示>
,并将整个内容包装在main
函数中,并修改了第二个线程以在stderr吐出东西:
import os
import select
import sys
import time
from threading import Thread
def threadOne():
while True:
print("Thread 1")
time.sleep(1)
def threadTwo():
while True:
# python 2 print does not support file argument like python 3,
# so writing to sys.stderr directly to simulate error message.
sys.stderr.write("Thread 2\n")
time.sleep(1)
# Run the threads in the background
threadOne = Thread(target = threadOne)
threadOne.daemon = True
threadTwo = Thread(target = threadTwo)
threadTwo.daemon = True
def main():
threadOne.start()
threadTwo.start()
tty_in = open('/dev/tty', 'r')
tty_out = open('/dev/tty', 'w')
fn = tty_in.fileno()
poll = select.poll()
poll.register(fn, select.POLLIN)
userInput = ""
chars = []
prompt = True
while True:
if os.getpgrp() == os.tcgetpgrp(fn) and poll.poll(10): # 10 ms
# poll should only return if the input buffer is filled,
# which is triggered when a user enters a complete line,
# which lets the following readline call to not block on
# a lack of input.
userInput = tty_in.readline()
# This should allow us to exit out
if userInput.strip() == "quit":
sys.exit()
# alternatively an empty string from Ctrl-D could be the
# other exit method.
else:
tty_out.write("user input: %s\n" % userInput)
prompt = True
elif not os.getpgrp() == os.tcgetpgrp(fn):
time.sleep(0.1)
if os.getpgrp() == os.tcgetpgrp(fn):
# back to foreground, print a prompt:
prompt = True
if prompt:
tty_out.write('> ')
tty_out.flush()
prompt = False
if __name__ == '__main__':
try:
# Uncomment if you are expecting stdin
# print('input stream length: %d ' % len(sys.stdin.read()))
main()
except KeyboardInterrupt:
print("Forcibly interrupted. Quitting")
sys.exit() # maybe with an error code
这是一个有趣的练习;我可以说,这是一个相当不错且有趣的问题.
Has been an interesting exercise; this was a rather good and interesting question, if I may say.
最后一点:这不是跨平台的,因为它没有select.poll
和/dev/tty
,因此在Windows上将不起作用.
这篇关于在后台运行时,Python脚本会挂起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!