Python 多处理 - 池
Posted
技术标签:
【中文标题】Python 多处理 - 池【英文标题】:Python multiproccessing - pool 【发布时间】:2021-07-24 14:20:23 【问题描述】:好吧,对于这个最小的例子,我们有一个主窗口和一个 qdialog 窗口。每一个都有一个按钮。
当用户点击主窗口的按钮时,qdialog 窗口打开。
当用户单击 qdialog 的按钮时,我想在多处理(异步)中从主窗口运行一个函数。但不是这些,而是再次打开一个新的主窗口。
代码:
untitled.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'untitled.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(318, 41)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.open_dialog = QtWidgets.QPushButton(self.centralwidget)
self.open_dialog.setObjectName("open_dialog")
self.gridLayout.addWidget(self.open_dialog, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.open_dialog.setText(_translate("MainWindow", "Open Dialog"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
dialog.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'dialog.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(400, 41)
self.gridLayout = QtWidgets.QGridLayout(Dialog)
self.gridLayout.setObjectName("gridLayout")
self.call_method_button = QtWidgets.QPushButton(Dialog)
self.call_method_button.setObjectName("call_method_button")
self.gridLayout.addWidget(self.call_method_button, 0, 0, 1, 1)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.call_method_button.setText(_translate("Dialog", "Call method from main Window"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())
main.py
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from untitled import *
from dialog import Ui_Dialog
from dialog_code import Dialog_Code
class MainCode:
def __init__(self):
self.app = QtWidgets.QApplication(sys.argv)
self.mainWindow = QtWidgets.QMainWindow()
self.main_ui = Ui_MainWindow()
self.main_ui.setupUi(self.mainWindow)
self.mainWindow.show()
self.main_ui.open_dialog.clicked.connect(self.open_dialog_window)
sys.exit(self.app.exec_())
def open_dialog_window(self):
self.dialog_window = QtWidgets.QDialog(self.mainWindow)
self.ui_dialog_window = Ui_Dialog()
self.ui_dialog_window.setupUi(self.dialog_window)
self.dialog_window.show()
dialog_window_run_code = Dialog_Code(self)
def print_ok_123(self):
print("print_ok_123 method")
for i in range(0,10):
print("Ok")
return 1
program = MainCode()
dialog_code.py
from multiprocessing import Pool
class Dialog_Code:
def __init__(self,main_self):
print("Dialog init")
self.main_self = main_self
self.main_self.ui_dialog_window.call_method_button.clicked.connect(lambda:self.call_main_method())
def call_main_method(self):
print("Button pressed")
self.pool = Pool(processes=1)
result = self.pool.apply_async(self.main_self.print_ok_123, tuple(), self.print_ok_end_123)
self.pool.close()
self.pool.join()
def print_ok_end_123(self):
print("End of main method")
要运行代码运行python main.py
pool 语句有什么问题,我该如何解决?
【问题讨论】:
我无法重现新主窗口再次显示的问题。此外,您似乎没有正确使用池,因为该函数根本没有执行(我从未使用过多处理,但从apply_async
文档的外观来看,您没有正确使用参数)。
【参考方案1】:
如果您调用的工作函数除了self
之外不接受其他参数并且愿意阻塞直到返回结果,那么使用方法apply
会更简单:
result = self.pool.apply(self.main_self.print_ok_123)
这相当于使用非阻塞apply_async
这种方式:
async_result = self.pool.apply_async(self.main_self.print_ok_123)
# block until the result is ready and get the result:
result = async_result.get()
关键是调用self.pool.apply_async(self.main_self.print_ok_123)
不会产生调用self.main_self.print_ok_123
的返回值,而是一个AsyncResult
实例,您必须调用它的get
方法来获取实际返回值。因此,虽然对 self.pool.close()
和 self.pool.join()
的调用将阻塞,直到所有未完成的任务完成,如果不对 AsyncResult
对象调用 get
,就无法从对 self.main_self.print_ok_123
的调用中获取实际返回值.
此外,如果您现在使用方法 apply
,根据定义,该方法会阻塞直到任务完成,严格来说,您不必必须调用close
和join
。如果你现在想强制清理你的池,你可以调用self.pool.terminate()
,或者你可以等待池被垃圾回收,当不再有任何引用时。
更新
import multiprocessing as mp
class Foo():
@staticmethod
def work1():
return 1
def work2(self):
return 2
if __name__ == '__main__':
pool = mp.Pool(1)
foo = Foo()
print(pool.apply(foo.work1))
print(pool.apply(foo.work2))
打印
1
2
【讨论】:
感谢您的回复。我在这里提出了一个新的类似问题:***.com/questions/67357180/… @ChrisP 在没有深入研究您的代码的情况下,我质疑通过创建大小为 1 的池然后让线程阻塞直到提交的任务完成可以获得什么。首先,除非返回值非常重要,否则只创建一个Process
会更便宜。但是,为什么不直接调用函数呢?为什么需要在新进程中执行?
您可以在这里看到完整的问题:youtube.com/watch?v=9emMPLo46KY,之后我将 pyaudio 流放在工作线程中,现在只有工作人员发出的信号冻结(时间线、时间、音量强度 - 所有显示) 当用户点击视频底部的两个按钮之一时。
我在调用 get() 时出现 TypeError --> TypeError: cannot pickle 'QApplication' object
怎么了?
首先,该链接已有 9 年历史。其次,贴有高度更新答案的代码是错误的:用@staticmethod
装饰的方法不接受self
参数。使用静态方法和常规方法查看我的更正代码更新,没有问题。以上是关于Python 多处理 - 池的主要内容,如果未能解决你的问题,请参考以下文章