Python脚本在后台运行时挂起
我有一个Python脚本(在2.7上运行),当我从命令行和后台运行它时,它的行为不同。当我从终端运行它时,它按预期运行,两个线程作为守护进程运行,将输出写入窗口,而主循环等待quit命令。它将永远运行,直到我进入退出:Python脚本在后台运行时挂起,python,multithreading,Python,Multithreading,我有一个Python脚本(在2.7上运行),当我从命令行和后台运行它时,它的行为不同。当我从终端运行它时,它按预期运行,两个线程作为守护进程运行,将输出写入窗口,而主循环等待quit命令。它将永远运行,直到我进入退出: python test.py 当同一程序在后台运行时,两个线程运行一次,然后程序挂起(我把范围缩小到了原始输入,我猜我做出了一个错误的假设,即即使在后台运行,两个线程也会继续运行,而原始输入阻止了主线程。例如,这两个线程基本上会永远运行,因为在这个场景中没有输入) 我的目标是让
python test.py
当同一程序在后台运行时,两个线程运行一次,然后程序挂起(我把范围缩小到了原始输入,我猜我做出了一个错误的假设,即即使在后台运行,两个线程也会继续运行,而原始输入阻止了主线程。例如,这两个线程基本上会永远运行,因为在这个场景中没有输入)
我的目标是让一个程序运行这些循环(可能永远运行),但如果我从终端运行它,它将接受输入
为了让程序从终端/后台运行,我是否需要在原始输入之前放置一个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()
为了让程序从终端/后台运行,我是否需要在原始输入之前放置一个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()
在某种程度上,这可能是可行的(我假设您是在*nix上运行的),但是如果用户要将进程发送回后台(即使用CtrlZ挂起它,然后在后台使用%&
)当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组的id,然后获取与此进程的标准输出相关联的进程组,如果匹配,则表示此进程当前处于前台,这意味着您可能可以调用raw\u input
,而不阻塞线程
另一个问题提出了类似的问题,我有一个较长的解释:
更好的方法是将其与标准I/O相结合,并分别处理交互式I/O(直接使用
/dev/tty
),因为您不希望stdin/stdout重定向被标准I/O“污染”。以下是包含这两种思想的更完整版本:
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
,如果有任何输入发送到shell,并且这会触发读线,读线随后将停止进程
此解决方案的优点是,在raw_input
trap和blocksstdin
停止进程(因为poll
检查是否有要读取的输入)之前,不需要用户点击enter并快速暂停任务以将其发送回后台,并允许正确的stdin/stdout重定向(由于所有交互式输入都是通过/dev/tty
处理的),因此用户可以执行以下操作:
$ python script.py < script.py 2> stderr
input stream length: 2116
这是一个有趣的练习;如果我可以说的话,这是一个相当好和有趣的问题
最后一点注意:这不是跨平台的,它不会在Windows上工作,因为它没有
select.poll
和/dev/tty谢谢你的回答和清晰的解释,非常感谢。这会起到作用,在我的Ubuntu和Raspberry Pi安装上都进行了测试,现在它的表现似乎像我的w在为.@b.pell拍摄时,我还必须感谢您以简洁但足够详细的方式对问题进行措辞,以向我们所有人展示手头的确切问题。我还更新了解决方案,以便更自由地将其发送回后台供用户使用。
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