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
itIsUnbelievable
V2EX  ›  Python

Pyqt5 多线程 QThread 要如何创建多个线程呢?

  •  
  •   itIsUnbelievable · 2019-08-22 11:33:42 +08:00 · 5562 次点击
    这是一个创建于 1919 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我写了一个类继承 QObject,然后用 movetothread 将前面那个实例放入一个继承 Qthread 的线程类,通过信号槽来连接。但是由于我接触 pyqt 没多久,对多线程这块不太熟,目前我只知道创建一个子线程将 ui 和业务逻辑分开,不知道怎么创建多个线程同时处理。我的业务场景是点击一个按钮同时对 16 个文件夹中的视频文件进行人脸分析,用 opencv 和百度云人脸识别 api。现在卡在如何创建多个线程这里了,第一次发帖,希望 v 站的老哥给点建议。非常感谢。

    14 条回复    2019-08-24 10:32:43 +08:00
    chengxiao
        1
    chengxiao  
       2019-08-22 11:46:07 +08:00
    Qthread 里只放线程里的运行代码,要多个线程就直接循环创建多个 Qthread 类实例就好了啊
    itIsUnbelievable
        2
    itIsUnbelievable  
    OP
       2019-08-22 12:05:29 +08:00
    @chengxiao
    ```
    class BaiduYunObject(QObject):
    stop_procession_signal=pyqtSignal()

    def __init__(self,parent=None):
    super(BaiduYunObject,self).__init__(parent)

    def main(self):
    print(11)
    self.stop_procession_signal.emit()



    class BaiduYunProcessingThread(QObject):
    def __init__(self,detect_url,search_url,add_url,multi_search_url):
    super().__init__()
    self.process_thread= QThread()
    self.process_thread1= QThread()
    self.procession=BaiduYunObject()
    self.procession.moveToThread(self.process_thread1)
    self.procession.moveToThread(self.process_thread)
    self.process_thread.started.connect(self.procession.main)
    self.process_thread1.started.connect(self.procession.main)
    self.procession.stop_procession_signal.connect(self.stop_process) #线程结束信号槽
    self.process_thread.start()
    self.process_thread1.start()

    def stop_process(self):
    self.process_thread.quit()

    ```
    我创建了两个线程实例测试了下,是能打印出两个 11.但是 11 前面报了下面这个错误:
    QObject::moveToThread: Current thread (0x1d0369badf0) is not the object's thread (0x1d068d35530).
    Cannot move to target thread (0x1d068d35f50)

    所以我有点疑问,不知道这样创建合不合理。而且还有个问题是如何使用循环创建多个线程实例,比如创建 16 个线程应该如何用 for 循环动态创建变量,毕竟 format()只能用于字符串,如果想要循环创建形如 process_thread_1,process_thread_2 这样的变量该怎么创建呢,我试过 locals(),但没成功,希望能给点建议。
    itIsUnbelievable
        3
    itIsUnbelievable  
    OP
       2019-08-22 12:53:26 +08:00
    @chengxiao 而且我把 main()函数改成
    for i in range(10):
    print(i)
    发现两个线程不是并发的,而是一个线程运行结束后再运行另一个。
    输出为 01234567890123456789
    chengxiao
        4
    chengxiao  
       2019-08-22 13:08:37 +08:00
    额....
    @itIsUnbelievable 因为你的程序没有写进 Qhtread 里啊 缩进太乱没看懂
    不过我感觉你的 BaiduYunObject 应该继承 Qthread 而不是 Qobject
    然后生成的 BaiduYunObject 的实例就是线程运行
    BaiduYunProcessingThread 这个也写的有问题 建议还是看看 pyqt 的线程案例照着写一个吧
    allenforrest
        5
    allenforrest  
       2019-08-22 13:13:07 +08:00   ❤️ 1
    可以改用 QThreadPool 和 QRunnable,管理线程更简单。
    每个要处理的任务,都定义成 QRunnable 对象的子类,实现 Run 方法。
    然后主线程创建 threadPool,每次要创建一个线程干活的时候,就 start 一个 QRunnable 对象即可,把对象的 autoDelete 设置为 true,run 跑完就自动销毁回收线程了。
    itIsUnbelievable
        6
    itIsUnbelievable  
    OP
       2019-08-22 13:25:26 +08:00
    @chengxiao 继承 Qthread 然后运行 run()方法的那种写法我之前试过,不过 google 了好久发现说 QT 的作者不支持这种写法,所以改成继承了 QObject 这种写法。https://blog.csdn.net/qq_39607437/article/details/79213717 他是这么说的:
    ```
    QtCore.QThread 是一个管理线程的类,当我们使用其构造函数的时候,便新建了一个线程。这里要强调,QThread 是一个线程管理器,不要把业务逻辑放在这个类里面,Qt 的作者已经多次批评继承 QThread 类来实现业务逻辑的做法。
    ```
    我就是按照这篇修改了写法。但是他也没讲这种写法创建很多个线程要怎么弄,所以我就试着创建多个线程实例,并将负责复杂计算业务逻辑的那个类实例放入线程类当中,现在的结果就如上面所说,线程好像不是同时运行的。 我现在很懵逼
    itIsUnbelievable
        7
    itIsUnbelievable  
    OP
       2019-08-22 13:33:09 +08:00
    重新发下这部分代码:在 UI 主线程实现点击按钮后创建一个 BaiduYunProcessingThread 类实例。
    class BaiduYunObject(QObject):
    stop_procession_signal=pyqtSignal()

    def __init__(self,parent=None):
    super(BaiduYunObject,self).__init__(parent)

    def main(self):
    for i in range(10):
    print(i)
    self.stop_procession_signal.emit()

    class BaiduYunProcessingThread(QObject):
    def __init__(self):
    super().__init__()
    self.process_thread= QThread()
    self.process_thread_1= QThread()
    self.procession=BaiduYunObject()
    self.procession.moveToThread(self.process_thread_1)
    self.procession.moveToThread(self.process_thread)
    self.process_thread.started.connect(self.procession.main)
    self.process_thread_1.started.connect(self.procession.main)
    self.procession.stop_procession_signal.connect(self.stop_process) #线程结束信号槽
    self.process_thread.start()
    self.process_thread_1.start()

    def stop_process(self):
    self.process_thread.quit()
    itIsUnbelievable
        8
    itIsUnbelievable  
    OP
       2019-08-22 13:37:19 +08:00
    @allenforrest 好的,非常感谢,我先去了解一下,感觉好像很有用。
    allenforrest
        9
    allenforrest  
       2019-08-22 13:40:52 +08:00
    @itIsUnbelievable 对,这代码多干净:

    #include <QCoreApplication>
    #include <QThreadPool>
    #include <QThread>
    #include <QRunnable>
    #include <QDebug>
    class MyRun : public QRunnable {
    public:
    void run() {
    int i=3;
    while(i) {
    i--;
    QThread::msleep(500);
    }
    }
    };
    int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    QThreadPool m;
    MyRun *run=new MyRun;
    if(!run->autoDelete()) {
    run->setAutoDelete(true);
    }
    m.start(run);

    m.waitForDone();
    return 0;
    }
    itIsUnbelievable
        10
    itIsUnbelievable  
    OP
       2019-08-22 14:02:16 +08:00
    @allenforrest 我刚才看到这样一句话:
    ```
    需要注意的是,QRunnable 不是一个 QObject,因此也就没有内建的与其它组件交互的机制。为了与其它组件进行交互,你必须自己编写低级线程原语,例如使用 mutex 守护来获取结果等。
    ```
    我想问下 QRunnable 支不支持信号槽机制呢?还有就是它可不可以在子线程中继续创建子线程?这个时候线程的执行顺序该如何保证呢?是要用 python 的 queue 吗?
    allenforrest
        11
    allenforrest  
       2019-08-23 15:19:57 +08:00
    @itIsUnbelievable 可以同时继承 QRunnable 和 QObject 哈
    woshidag
        12
    woshidag  
       2019-08-23 16:01:10 +08:00   ❤️ 1
    itIsUnbelievable
        13
    itIsUnbelievable  
    OP
       2019-08-24 10:27:03 +08:00
    @allenforrest 谢谢,我昨天看了一篇 QT 的文章就是继承了 QObject,我也就照着做了。而且在子线程中又创建了一个 threadPool,想要创建子线程就再 new 一个新的 runnable 类,测试了一下是可以在子线程下再创建子线程的。就是不太清楚线程中的共享数据问题
    itIsUnbelievable
        14
    itIsUnbelievable  
    OP
       2019-08-24 10:32:43 +08:00
    @woshidag 这篇讲的也太好了吧,amazing,谢谢老哥!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3904 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 05:23 · PVG 13:23 · LAX 21:23 · JFK 00:23
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.