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_())

ma​​in.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,根据定义,该方法会阻塞直到任务完成,严格来说,您不必必须调用closejoin。如果你现在想强制清理你的池,你可以调用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 多处理 - 池的主要内容,如果未能解决你的问题,请参考以下文章

Python 多处理 - 池

python中的多处理[破池进程]

python多处理池阻塞主线程

python多处理池:我怎么知道池中的所有工作人员何时完成?

处理对象方法的Python3多处理池未获取对象的更新数据

python多处理池,等待进程并重启自定义进程