我有一个Python脚本(在2.7上运行),当我从命令行和后台运行它时,它的行为有所不同。当我从终端运行它时,它按预期运行,两个线程作为守护进程运行,将输出写入窗口,而主循环等待退出命令。它一直运行直到我退出:
python test.py
当同一个程序在后台运行时,两个线程运行一次然后程序挂起(我把它缩小到raw_input,我想我做了一个错误的假设,两个线程将继续即使在后台运行并且raw_input阻塞了主要的一个。例如,由于在这种情况下没有输入,因此两个线程基本上会永远运行。)
python test.py &
我的目标是让一个程序运行这些循环(可能永远)但如果我从终端运行它将接受输入。
为了允许程序从终端/后台运行,我是否需要在raw_input之前基本放置一个if语句,检查它是否在后台或者我是否遗漏了其他会有帮助吗?
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()
答案 0 :(得分:3)
为了允许程序从终端/后台运行,我是否需要在raw_input之前基本放置一个if语句,检查它是否在后台或者我是否遗漏了其他会有帮助吗?
在某种程度上这个可能有效(我假设你在* nix上运行它),但是如果用户将进程备份发送到后台(即使用 Ctrl挂起它) Z 然后使用%&
在后台恢复它,而raw_input
正在等待用户输入,然后stdin
上的读取将被阻止为它在后台,因此导致内核停止进程,因为这是stdio的工作方式。如果这是可以接受的(基本上用户必须在暂停进程之前按Enter键),你可以这样做:
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
。
另一个问题提出了类似问题,我在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()
仍然需要后台/前台检测,因为进程未完全与shell分离(因为它可以返回到前台),因此poll
将返回tty的fileno
if任何输入都被发送到shell中,如果这会触发readline,那么它将停止进程。
此解决方案的优点是不需要用户点击进入并快速暂停任务,以便在raw_input
陷阱之前将其发送回后台并阻止stdin
停止进程(如{{ 1}}检查是否有要读取的输入),并允许正确的stdin / stdout重定向(因为所有交互式输入都是通过poll
处理的),因此用户可以执行以下操作:
/dev/tty
在下面的完整示例中,它还向用户提供提示,即无论何时发送命令或每当进程返回到前台时都会显示$ python script.py < script.py 2> stderr
input stream length: 2116
,并将整个事件包装在{{1函数,并修改第二个线程以在stderr中吐出东西:
>
一直是一项有趣的运动;如果我可以说,这是一个相当好的和有趣的问题。
最后一点说明:这不是跨平台的,它不适用于Windows,因为它没有main
和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
。