研二硕, Python +pyqt,多进程问题求助 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
gangdong
V2EX    Python

研二硕, Python +pyqt,多进程问题求助

  •  
  •   gangdong 2020-10-13 17:02:10 +08:00 6121 次点击
    这是一个创建于 1824 天前的主题,其中的信息可能已经有所发展或是发生改变。
    现在的毕业课题就是构建一个数据处理软件。属于 cpu 密集型。现在的版本是 python+pyqt 构建的。
    因为是 GIL 的关系,不能利用多线程,而且我在 UI 线程中没有办法创建多进程就很蛋疼。
    现在我的需求是能够并发(多线程貌似行不通),多进程在 UI 中无法调用。
    目前我的就计划有几个:
    1. html 界面+python 后端运行(这样应该可以多进程把?是否有老哥有相关经验讲一下)
    2. 更换语言做(本人非科班,cpp 皮毛,难度挺大,而且后面还要加入一些聚类算法之类的,python 比较方便)
    3. 混编?许需要多线程的地方使用 c 重写?这是我查到的方法,就是不知道应该怎么学习这个混编的知识。
    谢谢。
    68 条回复    2020-10-16 15:02:16 +08:00
    coolrice1
        1
    coolrice1  
       2020-10-13 17:57:46 +08:00
    QRunnable 和 QThreadPool 可以用吗?
    gangdong
        2
    gangdong  
    OP
       2020-10-13 18:10:56 +08:00
    @coolrice1
    对于 pyqt 来说,多用多线程也是会有 GIL 的影响,也就是虚假的多线程
    lyhapple
        3
    lyhapple  
       2020-10-13 18:14:47 +08:00
    如果是 python3, 为什么不用协程?
    lpts007
        4
    lpts007  
       2020-10-13 18:26:21 +08:00
    "而且我在 UI 线程中没有办法创建多进程就很蛋疼" 这句话怎么回事,是你写错了吧!

    很多年前我写的小东西,启动 ui,其中几个按钮是长耗时功能(查数据库,长时间计算),就是点击以后用多进程进行的计算的,没啥问题啊。
    ytymf
        5
    ytymf  
       2020-10-13 18:30:23 +08:00
    可以把你的计算任务与 QT 界面完全的解耦,独立启动。二者通过 socket 或者 pipe 通信就好了。没有必要非要当成一个工程。
    jin7
        6
    jin7  
       2020-10-13 18:31:19 +08:00
    楼上说得在理
    gangdong
        7
    gangdong  
    OP
       2020-10-13 18:31:53 +08:00
    @lyhapple
    这个我学习一下,谢谢
    menc
        8
    menc  
       2020-10-13 18:32:02 +08:00
    拆分两个项目,rpc 调用即可
    gangdong
        9
    gangdong  
    OP
       2020-10-13 18:33:13 +08:00
    @lpts007
    或许是我的问题?我在 qt 的 ui 线程里面确实没有办法创建其他的进程(逻辑上进程不是比线程更高一个层次吗)
    gangdong
        10
    gangdong  
    OP
       2020-10-13 18:34:11 +08:00
    @ytymf
    你好,谢谢你的回复,两者间需要一些数据的传输,您说的这两个方案我先学习一下。
    gangdong
        11
    gangdong  
    OP
       2020-10-13 18:34:36 +08:00
    @menc
    谢谢,我查查您说的方案。
    lpts007
        12
    lpts007  
       2020-10-13 18:34:45 +08:00
    @gangdong 不要学
    gangdong
        13
    gangdong  
    OP
       2020-10-13 18:37:37 +08:00
    @lpts007
    什么。。。。
    jin7
        14
    jin7  
       2020-10-13 18:37:48 +08:00
    你搞不定的话 可付费帮你搞定 刚帮人搞定了一样的问题 不喜忽视
    lpts007
        15
    lpts007  
       2020-10-13 18:40:51 +08:00
    @gangdong
    “我在 qt 的 ui 线程里面确实没有办法创建其他的进程” 何出此言,报什么错
    gangdong
        16
    gangdong  
    OP
       2020-10-13 18:42:38 +08:00
    @jin7
    可以讲一下思路吗,学习学习额
    renmu123
        17
    renmu123  
       2020-10-13 18:44:00 +08:00 via Android
    gill 是没办法利用多核,只是多线程是伪多线程,出来 io 阻塞没有问题
    jin7
        18
    jin7  
       2020-10-13 18:45:38 +08:00
    @gangdong #16 楼上都讲了 就是那样 只是怎么弄 还要看你项目的具体情况
    ClutchBear
        19
    ClutchBear  
       2020-10-13 18:53:29 +08:00
    flask 后端,
    vue 前端
    gunicorn 开多个进程, 也不慢的.
    knightdf
        20
    knightdf  
       2020-10-13 19:28:15 +08:00
    > 而且我在 UI 线程中没有办法创建多进程就很蛋疼。

    可以用啊, 我写过一个小软件就是 qt+multiprocessing
    AX5N
        21
    AX5N  
       2020-10-13 20:01:30 +08:00
    我觉得和 c 混编比较好,就看你有没有能力把重计算部分用 c 来写了。根据你项目的复杂度的不同,不见得有必要一定要让 c 来完成多线程。cpu 密集型不建议纯 python,除非你换解释器,否则即便把 python 玩出花,2 30 倍的性能差距还是跑不掉的。
    AX5N
        22
    AX5N  
       2020-10-13 20:06:10 +08:00
    @AX5N 调用 C 库很简单,你用 C 编写个函数,并且将这个函数导出来,然后用 python 去调用这个函数就行了。你可以在 python 上用多线程(协程)去调用这个函数,重活都丢给 c 去干。
    gangdong
        23
    gangdong  
    OP
       2020-10-13 21:11:30 +08:00
    @knightdf
    你好,在我使用多进程的时候,没成功过。请问你的软件多进程部分的代码可以参考一下吗
    gangdong
        24
    gangdong  
    OP
       2020-10-13 21:15:56 +08:00
    @AX5N 是的老哥,我认真分析了我的程序,其中计算部分的一部分我使用 numpy 来完成的,这部分估计我写也不会比 numpy 更好。另一个需要计算的部分是无法避免的循环,循环的话,c 和 python 不知道是否有较大的性能差距,后面我测试一下。谢谢老哥的认真回复。
    gangdong
        25
    gangdong  
    OP
       2020-10-13 21:17:10 +08:00
    @ClutchBear 这样是一个思路,现在界面是使用 qt 做的,这条路行不通了,再试试这个。谢谢。
    gladuo
        26
    gladuo  
       2020-10-13 22:07:44 +08:00
    混编的话搜索引擎关键词『 pybind11 』
    gladuo
        27
    gladuo  
       2020-10-13 22:09:28 +08:00
    楼上说的方案就是你 1. 的方案,只不过界面还是沿用 pyqt 而不用换成 html,计算的部分独立作为服务就好
    AX5N
        28
    AX5N  
       2020-10-13 22:31:36 +08:00
    @gangdong 据我所知,c 可以调用 numpy https://numpy.org/devdocs/user/c-info.html
    knightdf
        29
    knightdf  
       2020-10-13 22:35:27 +08:00
    @gangdong 在 QThread 里开多进程计算
    gangdong
        30
    gangdong  
    OP
       2020-10-13 22:50:50 +08:00
    @knightdf 目前我是在 ui 里使用 QThread 开一个线程进行计算,这个应该是线程没问题吧? 你的意思是在 QThread 这个新的线程里再开启多进程?
    gangdong
        31
    gangdong  
    OP
       2020-10-13 22:52:09 +08:00
    @AX5N 谢谢,我学习学习!
    gangdong
        32
    gangdong  
    OP
       2020-10-13 22:52:20 +08:00
    @gladuo 谢谢!
    snowydec
        33
    snowydec  
       2020-10-13 23:18:22 +08:00
    建议先把版本升级到研三,应该就解决了
    BingoXuan
        34
    BingoXuan  
       2020-10-13 23:33:54 +08:00 via Android   1
    我们公司架构就是 pyqt 启动界面时候开多进程做计算引擎,用 zmq 来做 ipc,非常简单方便
    frankjoe
        35
    frankjoe  
       2020-10-14 00:22:37 +08:00 via Android
    QRunnable 和 QThreadPool 可以用吗?
    gangdong
        36
    gangdong  
    OP
       2020-10-14 08:08:14 +08:00
    @snowydec 哈哈哈哈谢谢老哥
    gangdong
        37
    gangdong  
    OP
       2020-10-14 08:09:52 +08:00
    @BingoXuan 谢谢,我在自己尝试一下。一直没有把 pyqt 开启多进程搞定
    knightdf
        38
    knightdf  
       2020-10-14 09:35:09 +08:00
    @gangdong 是啊,避免堵塞 UI 线程,在新的线程里做
    no1xsyzy
        39
    no1xsyzy  
       2020-10-14 10:15:30 +08:00
    刚随便试了下,除了用 QTimer 非阻塞下了点功夫外没什么问题……
    但发现一个坑点:请确保创建 GUI 的代码在 __name__ == '__main__' 下,以及不要拉升 Queue 的位置,一旦拉升就要 pickle,但 Queue 不能 pickle
    https://gist.github.com/no1xsyzy/03fb6d436dc3e87a1d2d12505d9234fe
    no1xsyzy
        40
    no1xsyzy  
       2020-10-14 10:25:24 +08:00
    好像也不是拉升就要 pickle,刚才 p.start() 一直报 Queue 不能 pickle……
    顺便修了下之前运行中再次点击按钮导致卡死的问题
    gangdong
        41
    gangdong  
    OP
       2020-10-14 14:15:45 +08:00
    @no1xsyzy 谢谢 ,我学习一下。
    iqxd
        42
    iqxd  
       2020-10-14 14:53:56 +08:00
    好像 windows 和 linux 下的 python 多进程是有些区别的,linux 的底层是 fork. 如果是调用了 numpy 做计算,用线程调用影响也没那么大吧,numpy 在 C 层面是会用到多核计算的.
    gangdong
        43
    gangdong  
    OP
       2020-10-14 16:13:05 +08:00
    @iqxd 关于最后一句我刚开始也是这样想的,但是我自己看 cpu 占用的时候,确实只有一个核心
    E520
        44
    E520  
       2020-10-14 18:32:02 +08:00
    易语言
    gangdong
        45
    gangdong  
    OP
       2020-10-14 21:04:56 +08:00
    @BingoXuan 你好,请问怎么在 qt 启动的时候开启多进程呢
    gangdong
        46
    gangdong  
    OP
       2020-10-14 21:36:28 +08:00
    @knightdf 你好,我今天尝试了在 ui 线程里创建新的计算线程,然后在计算线程里开启多进程。还是有问题。
    我查了一下,问题大概是:在 windows 上子进程 一定要放在 if __name__ == '__main__' 下面。
    您当初的程序是在 windows 上开发的吗
    BingoXuan
        47
    BingoXuan  
       2020-10-14 22:24:44 +08:00 via Android
    @gangdong
    主进程先启动计算进程,然后进去界面 main 函数,等待界面 main 结束后就传递信号给计算进程结束任务,最后关闭所有资源。
    gangdong
        48
    gangdong  
    OP
       2020-10-14 22:55:36 +08:00 via iPhone
    @no1xsyzy 你好,我看你的代码的时候有一个地方我没在其他教程里面见过,就是 @btn.clicked.connect 这个装饰器的写法,请问是自创的吗 哪里可以看到这种写法呢
    no1xsyzy
        49
    no1xsyzy  
       2020-10-15 09:16:13 +08:00
    @gangdong 至少我是看 Python3.9 的 What's New 时看到的……
    https://www.python.org/dev/peps/pep-0614/
    gangdong
        50
    gangdong  
    OP
       2020-10-15 09:46:08 +08:00
    @no1xsyzy 谢谢。
    gangdong
        51
    gangdong  
    OP
       2020-10-15 10:43:43 +08:00
    @knightdf
    import sys
    from PyQt5.QtWidgets import QMainWindow,QApplication,QWidget
    from untitled import *
    from PyQt5.QtWidgets import QFileDialog
    from multiprocessing import Pool
    from PyQt5.QtCore import QObject,pyqtSignal,QThread
    import time,os


    class LoadFiles(QObject):
    run_end=pyqtSignal()
    def __init__(self):
    super(LoadFiles, self).__init__()
    def run(self):
    testFL = [1, 2, 3, 4, 5, 6]
    pool = Pool(6)
    pool.apply_async(self.work)
    pool.close()
    pool.join()
    self.run_end.emit()

    def work(self):
    # fn: 函数参数是数据列表的一个元素
    fn=100
    print('当前进程: {}'.format(os.getpid()), time.time())
    time.sleep(10)
    print(fn * fn)


    class PrecessTest(QMainWindow,Ui_MainWindow):
    def __init__(self):
    super(PrecessTest, self).__init__()
    self.setupUi(self)
    self.pushButton.clicked.connect(self.load_files)

    def load_files(self):
    self._loadThread=QThread()
    self.loadThread=LoadFiles()
    self.loadThread.run_end.connect(self.stop_thread)
    self.loadThread.moveToThread(self._loadThread)
    self._loadThread.started.connect(self.loadThread.run)
    # openfile_name = QFileDialog.getOpenFileNames(self,'选择文件',"./","TDMS Files(*.tdms)")
    # print(openfile_name)
    self._loadThread.start()
    print('现在线程状态 :', self._loadThread.isRunning())

    def stop_thread(self):
    self._loadThread.quit()
    self._loadThread.wait()
    print('现在线程状态 :', self._loadThread.isRunning())
    if __name__ == '__main__':
    app=QApplication(sys.argv)
    ui=PrecessTest()
    ui.show()
    sys.exit(app.exec_())

    你好,我简单的写了一下,这样的话就是不能开多进程,因为开启多进程的部分在 windows 上应该在__main__:的下面,我就不知道如何操作了,主要的问题应该是在子线程下面无法创建多进程?
    我是新人,没办法使用 gist 添加外部链接不好意思。。。
    gangdong
        52
    gangdong  
    OP
       2020-10-15 10:48:49 +08:00
    @no1xsyzy
    能麻烦看看我上面的代码回复吗,我不知道如何让将创建进程池的部分代码放在 if __name__ == '__main__' 下面(为了避免 ui 堵塞,我得将计算部分新建一个线程,这样如何在这个新的线程中建立多进程呢...)
    gangdong
        53
    gangdong  
    OP
       2020-10-15 10:55:24 +08:00
    @lpts007
    您好,我在上面粘贴了我的代码,就是在 QThread 线程中的多进程是无效的
    knightdf
        54
    knightdf  
       2020-10-15 13:10:17 +08:00
    @gangdong 刚上 v2,我是 mac 下开发的,但是在 windows 上打包的,所以平台应该不影响。
    没缩进太难看了,贴个你的示例吧 https://gist.github.com/knightdf/657b5418a9ec11ec1138464b37d1c14c
    照你的逻辑手打的,可能 typo
    Asfy
        55
    Asfy  
       2020-10-15 13:32:07 +08:00
    和我毕业设计差不多啊
    量化分析,pyqt 展示
    跑个算法, 界面卡死.
    要用 python, 直接分两个服务 数据和界面.
    数据服务换语言写, 界面 python 搞, 甚至界面可以直接换掉.
    gangdong
        56
    gangdong  
    OP
       2020-10-15 17:20:07 +08:00 via iPhone
    @Asfy 界面的话。qt pyqt 都差不多,你是说后台的数据处理换语言是吧,请问你用的什么写的
    no1xsyzy
        57
    no1xsyzy  
       2020-10-15 20:45:35 +08:00
    @gangdong 你还是贴网络剪贴板里吧,C-like 还能自动格式化,Python 没缩进就彻底乱了。
    gangdong
        58
    gangdong  
    OP
       2020-10-15 21:28:56 +08:00
    @no1xsyzy
    你好,coder_gang 中间需要一个斜杠
    gangdong
        59
    gangdong  
    OP
       2020-10-15 21:29:58 +08:00
    @no1xsyzy
    这个是网络剪切板的后缀....
    gangdong
        60
    gangdong  
    OP
       2020-10-15 22:05:22 +08:00
    @knightdf
    我在 mac 上也试了,还是调用不成功,但是也不报错...
    knightdf
        61
    knightdf  
       2020-10-15 22:22:46 +08:00
    @gangdong python 和 qt 是什么版本啊?
    no1xsyzy
        62
    no1xsyzy  
       2020-10-15 22:33:17 +08:00
    @gangdong ……找了一圈才发现有个网络剪贴板名字就叫“网络剪贴板” …… 其实是指 pastebin[.]com 和 paste.ubuntu[.]com 这些

    还是先 https://stackoverflow.com/help/minimal-reproducible-example

    Pool 里不要传 bound method,会导致整个实例被 pickle,然而 QObject 不能被 pickle
    LoadFiles.work 搞成 staticmethod 或者 classmethod 或者干脆提出去当作单独函数处理。

    https://gist.github.com/no1xsyzy/ef171f987a2e78e5f831e523a14c7e9e#file-v2_714573_2-py-L30-L34

    你得明白一点,multiprocessing 的基础是 pickle,一切内容都是 pickle 之后通过管道传递到新进程里面去再解开使用,所以参与计算的任何内容都必须可以被 pickle 。但你继承了 C++ 的对象 QObject,就导致了它不能被 pickle (准确地说,它封装了一个 Qt 的 QObject,所以 classmethod 尽管封了一个 QObject 进去但是可以 pickle,因为类本身只有 binding 没有实际的 C++ 层对象)
    另外,还有一点就是非 fork() 的方式均会导致整个文件以其他的 __name__ 重新执行一遍,这就是为什么根据平台会要求 __name__ == '__main__',rev2 里我把 __name__ 在子进程打印出来了,你可以看下。
    gangdong
        63
    gangdong  
    OP
       2020-10-16 07:34:02 +08:00
    @knightdf
    python:3.8.5
    pyqt:5.12
    no1xsyzy
        64
    no1xsyzy  
       2020-10-16 09:05:17 +08:00
    @gangdong #60 报错的话,似乎 Qt 的封装会导致不打印错误。
    用 IDE 把错误捕获一下吧
    knightdf
        65
    knightdf  
       2020-10-16 09:52:17 +08:00
    @gangdong 那你参考下这个吧,给 windows 的 multiprocessing 打个 patch 试试
    https://github.com/pyinstaller/pyinstaller/wiki/Recipe-Multiprocessing
    gangdong
        66
    gangdong  
    OP
       2020-10-16 14:03:16 +08:00
    @no1xsyzy
    不好意思才回复,不知道为何今天 gist 一直访问不了,挂了梯子、改 host 也没用...
    看我什么时候能访问了看看你贴的示例.谢谢
    gangdong
        67
    gangdong  
    OP
       2020-10-16 14:26:47 +08:00
    @no1xsyzy
    输出的错误是:error cannot pickle 'LoadFiles' object 。 应该就是你说的这个意思了...
    gangdong
        68
    gangdong  
    OP
       2020-10-16 15:02:16 +08:00
    @no1xsyzy
    按照你的提示,我做了修改,https[:]//paste[.]ubuntu[.]com[/]p[/]NjSHMjk6sC,成功了,我正在看你讲的 pickle,谢谢你的帮助。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     928 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 19:12 PVG 03:12 LAX 12:12 JFK 15:12
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86