gpt4 book ai didi

Python 脚本在后台运行时挂起

转载 作者:行者123 更新时间:2023-11-28 16:30:02 24 4
gpt4 key购买 nike

我有一个 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()

最佳答案

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?

在某种程度上,这可能有效(我假设你在 *nix 上运行它),但是如果用户要将进程发送回后台(即使用 Ctrl 暂停它Z 然后在后台使用 %& 恢复它,而 raw_input正在等待用户输入,然后读取 stdin然后将被阻止,因为它在后台,从而导致内核停止进程,因为这是 stdio 的工作方式。如果这是可以接受的(基本上用户必须在暂停进程之前按回车键),您可以简单地这样做:

import os

while True:
userInput = ""
if os.getpgrp() == os.tcgetpgrp(sys.stdout.fileno()):
userInput = raw_input("")
time.sleep(1)

什么 os.getpgrp 做的是返回当前操作系统组的id,然后是 os.tcgetpgrp 获取与此进程的标准输出关联的进程组,如果它们匹配,则表示此进程当前在前台,这意味着您可以调用 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将返回 fileno如果有任何输入被发送到 shell,则 tty 的启动,如果这触发了 readline,它将停止进程。

此解决方案的优点是不需要用户按回车键并在 raw_input 之前快速暂停任务以将其发送回后台。陷阱和障碍 stdin停止进程(因为 poll 检查是否有要读取的输入),并允许正确的 stdin/stdout 重定向(因为所有交互式输入都是通过 /dev/tty 处理的)所以用户可以做类似的事情:

$ 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

是一个有趣的练习;如果我可以说,这是一个相当不错且有趣的问题。

最后一点:这不是跨平台的,它不能在 Windows 上运行,因为它没有 select.poll/dev/tty .

关于Python 脚本在后台运行时挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32899275/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com