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 · 5307 次点击
    这是一个创建于 1262 天前的主题,其中的信息可能已经有所发展或是发生改变。
    现在的毕业课题就是构建一个数据处理软件。属于 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   ·   我们的愿景   ·   实用小工具   ·   958 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 41ms · UTC 20:42 · PVG 04:42 · LAX 13:42 · JFK 16:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.