无法使用 Python 从 Windows 上生成的子进程读取标准输出
Unable to read stdout from spawned subprocess on Windows with Python
我正在尝试为一个非常简单的 windows 终端应用程序构建一个 GUI 前端(此处提供二进制文件 https://github.com/00-matt/randomx-stress/releases/download/200109/randomx-stress-windows-200109.zip),并使用 popen 将终端应用程序作为子进程启动并将输出输入一个队列,然后读取队列并将输出放入 tkinter GUI。尽管我无法从生成的子进程的 stdout 或 stderr 获得任何东西,但无论我尝试什么。这是我的代码的精简版:
from tkinter import *
import subprocess
import threading
import queue
import os
from os import system
from re import sub
import sys
os.environ["PYTHONUNBUFFERED"] = "1"
def enqueue_output(p, q):
while True:
out = p.stdout.readline()
if out == '' and p.poll() is not None:
break
if out:
print(out.strip(), flush=True)
q.put_nowait(out.strip())
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.started = False
self.p = None
self.q = queue.Queue()
self.threads = 1
self.init_window()
def init_window(self):
self.master.title("RandomX Stress Tester")
self.pack(fill=BOTH, expand=1)
self.hashrateLabel = Label(self, text="Hashrate: ")
self.hashrateLabel.after(2000, self.refresh_hashrate)
self.startButton = Button(self, text="Start", background="green", command=self.startstop)
self.quitButton = Button(self, text="Quit", command=self.client_exit)
self.hashrateLabel.place(x=50, y=220)
self.startButton.place(x=50, y=270)
self.quitButton.place(x=220, y=270)
def startstop(self):
if not self.started:
self.p = subprocess.Popen([r"randomx-stress.exe", "-t", str(self.threads)],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True,
encoding='utf-8',
errors='replace')
self.t = threading.Thread(target=enqueue_output, args=(self.p, self.q))
self.t.daemon = True
self.t.start()
self.started = True
self.startButton.config(text="Stop", background="red")
elif self.started:
system("taskkill /im randomx-stress.exe /f")
self.p.kill()
self.t.join()
self.started = False
self.startButton.config(text="Start", background="green")
def refresh_hashrate(self):
print("checking hashrate if running")
if not self.started:
pass
elif self.started:
print("its running")
try:
line = self.q.get_nowait()
print(line)
if 'H/s' in line:
hashrate = line.split(' ')[0]
self.hashrateLabel.text = "Hashrate: {0:.2f} h/s".format(hashrate)
except:
print("error")
self.hashrateLabel.after(2000, self.refresh_hashrate)
def client_exit(self):
exit()
root = Tk()
root.geometry("400x300")
app = Window(root)
root.mainloop()
我现在怀疑终端应用程序正在缓冲输出,我无法在 Windows 上绕过它。如果有人可以确认这是问题所在并提供解决方法,我将不胜感激!
基于 C 库的程序根据它是终端(假设用户需要逐行数据)还是管道(块缓冲区以提高效率)对 stdout 的处理方式不同。在类 unix 系统上,程序可能会被欺骗,认为它是 运行 针对具有伪 tty 设备的终端。在 Windows,Microsoft 从未实施过等效技术。子进程将阻塞缓冲区。
您可以通过在写入后刷新标准输出来在被调用程序中解决这个问题。这是在 C++ 中使用 endl
自动完成的。在你的情况下是:
std::cout << hashes / difference.count() << " H/s" << endl;
我正在尝试为一个非常简单的 windows 终端应用程序构建一个 GUI 前端(此处提供二进制文件 https://github.com/00-matt/randomx-stress/releases/download/200109/randomx-stress-windows-200109.zip),并使用 popen 将终端应用程序作为子进程启动并将输出输入一个队列,然后读取队列并将输出放入 tkinter GUI。尽管我无法从生成的子进程的 stdout 或 stderr 获得任何东西,但无论我尝试什么。这是我的代码的精简版:
from tkinter import *
import subprocess
import threading
import queue
import os
from os import system
from re import sub
import sys
os.environ["PYTHONUNBUFFERED"] = "1"
def enqueue_output(p, q):
while True:
out = p.stdout.readline()
if out == '' and p.poll() is not None:
break
if out:
print(out.strip(), flush=True)
q.put_nowait(out.strip())
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.started = False
self.p = None
self.q = queue.Queue()
self.threads = 1
self.init_window()
def init_window(self):
self.master.title("RandomX Stress Tester")
self.pack(fill=BOTH, expand=1)
self.hashrateLabel = Label(self, text="Hashrate: ")
self.hashrateLabel.after(2000, self.refresh_hashrate)
self.startButton = Button(self, text="Start", background="green", command=self.startstop)
self.quitButton = Button(self, text="Quit", command=self.client_exit)
self.hashrateLabel.place(x=50, y=220)
self.startButton.place(x=50, y=270)
self.quitButton.place(x=220, y=270)
def startstop(self):
if not self.started:
self.p = subprocess.Popen([r"randomx-stress.exe", "-t", str(self.threads)],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True,
encoding='utf-8',
errors='replace')
self.t = threading.Thread(target=enqueue_output, args=(self.p, self.q))
self.t.daemon = True
self.t.start()
self.started = True
self.startButton.config(text="Stop", background="red")
elif self.started:
system("taskkill /im randomx-stress.exe /f")
self.p.kill()
self.t.join()
self.started = False
self.startButton.config(text="Start", background="green")
def refresh_hashrate(self):
print("checking hashrate if running")
if not self.started:
pass
elif self.started:
print("its running")
try:
line = self.q.get_nowait()
print(line)
if 'H/s' in line:
hashrate = line.split(' ')[0]
self.hashrateLabel.text = "Hashrate: {0:.2f} h/s".format(hashrate)
except:
print("error")
self.hashrateLabel.after(2000, self.refresh_hashrate)
def client_exit(self):
exit()
root = Tk()
root.geometry("400x300")
app = Window(root)
root.mainloop()
我现在怀疑终端应用程序正在缓冲输出,我无法在 Windows 上绕过它。如果有人可以确认这是问题所在并提供解决方法,我将不胜感激!
基于 C 库的程序根据它是终端(假设用户需要逐行数据)还是管道(块缓冲区以提高效率)对 stdout 的处理方式不同。在类 unix 系统上,程序可能会被欺骗,认为它是 运行 针对具有伪 tty 设备的终端。在 Windows,Microsoft 从未实施过等效技术。子进程将阻塞缓冲区。
您可以通过在写入后刷新标准输出来在被调用程序中解决这个问题。这是在 C++ 中使用 endl
自动完成的。在你的情况下是:
std::cout << hashes / difference.count() << " H/s" << endl;