背景故事
我正在制作一个图形用户界面版本的加密矿工。 我已经完成了 UI,并且有一个命令行挖掘器,但是当我尝试从 python 启动它时,问题就开始了。 我喜欢线程,所以我添加它来启动挖掘过程并捕获输出以从中获取信息并将其显示给用户,同时仍然能够使用应用程序并与应用程序交互。起初它按预期工作,但出现了问题,那就是停止线程,但据我所知,没有正式的方法可以这样做。我转而使用多处理,认为它会起作用,但我知道它不起作用。 它说引发一个错误,说 EOFError: Ran out of input,这意味着即使在使用线程时也没有输出,它工作得很好。
总结
使用多处理时无法获取进程的输出,即使它与线程一起工作,启动时会引发错误
multiprocessing.Process()
这是代码片段
# start mining
def start_mining(self):
if self.mining_thread and self.mining_thread.is_alive():
print("Already started mining")
return
settings["mining"] = True
self.miningpage_frame.pack(fill="both")
self.homepage_frame.pack_forget()
mining_command = self.mining_cmd.get()
# Function to capture and display the miner output
def capture_output():
process = subprocess.Popen(mining_command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True,
text=True)
for line in process.stdout:
self.output_text.insert(tk.END, line)
self.output_text.see(tk.END) # Scroll to the end to show the latest output
# Check if mining should be stopped
if settings["mining"] == False:
break # Exit the loop and stop mining
process.wait()
return_code = process.returncode
if return_code == 0:
print("Mining process completed successfully.")
else:
print(f"Mining process failed with return code {return_code}.")
# Start the mining thread
self.mining_thread = threading.Thread(target=capture_output)
self.mining_thread.start()
#self.mining_process = multiprocessing.Process(target=capture_output)
#self.mining_process.start()
首先,不需要在新进程中运行
capture_output
,因为它在子进程中运行 mining_command
,并且只是对 stdout 输出进行简单处理,线程处理就足够了。
但是至于为什么在使用多处理时会出现异常,我猜你是在 Windows 或某些使用 spawn 方法创建子进程的平台下运行的。在 Windows 下,我希望您得到异常,因为您的类实例(无论类名是什么)需要被 pickle 到子进程,但是具有包含在
compute_method
等方法中的函数的类无法被 pickle。
但是即使你在没有这个限制的Linux下运行这个代码,代码仍然无法运行。新的子进程将更新在与主进程不同的地址空间中运行的类实例的副本;主进程的类副本保持不变。也就是说,主进程中的类实例副本不会被修改。
考虑以下最小的、可重现的示例:self.output_text
当在 Windows 下运行时,我们会得到一个异常:
from multiprocessing import Process
import subprocess
class Foo:
def run(self):
self.stdout_data = None
def capture_output():
process = subprocess.Popen("echo Hello!", stdout=subprocess.PIPE, shell=True,
text=True)
self.stdout_data, _ = process.communicate()
print('child process self.stdout_data = ', self.stdout_data, end='')
p = Process(target=capture_output)
p.start()
p.join()
print('main process self.stdout_data = ', self.stdout_data)
# To support platforms that create child processes using
# the "spawn" method (e.g. Windows):
if __name__ == '__main__':
foo = Foo()
foo.run()
在 Linux 下我们也不例外。然而,主进程的
...
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Program Files\Python38\lib\multiprocessing\spawn.py", line 116, in spawn_main
exitcode = _main(fd, parent_sentinel)
File "C:\Program Files\Python38\lib\multiprocessing\spawn.py", line 126, in _main
self = reduction.pickle.load(from_parent)
EOFError: Ran out of input
ForkingPickler(file, protocol).dump(obj)
AttributeError: Can't pickle local object 'Foo.run.<locals>.capture_output'
副本还没有更新。输出是:
foo