去年十二月份的时候,有人来店铺问我多线程的问题
他想重构一个图像处理的上位机软件,线程A接收数据后,线程B和线程C同时对数据进行 Detect,都完成后发给线程D
用 Plant UML 绘制客户需求,如下所示:
即线程 A-BC-D 串行执行,BC并行执行。
我在 https://github.com/abc881858/Camera 上添加了一个分支 Multithreading 来演示代码
最核心的语句就是
xworkerA.moveToThread(&threadA);
workerB.moveToThread(&threadB);
workerC.moveToThread(&threadC);
connect(this, &MainWindow::connectToServer, &workerA, &CameraClient::connectToServer);
connect(this, &MainWindow::requestImage, &workerA, &CameraClient::requestImage);
connect(this, &MainWindow::setParam, &workerA, &CameraClient::setParam);
connect(this, &MainWindow::getParam, &workerA, &CameraClient::getParam);
connect(&workerA, &CameraClient::brightness_signal, this, &MainWindow::brightness_slot);
connect(&workerA, &CameraClient::image_signal, &workerB, &WorkerB::image_slot);
connect(&workerA, &CameraClient::image_signal, &workerC, &WorkerC::image_slot);
connect(&workerB, &WorkerB::data_b_signal, this, &MainWindow::data_b_slot);
connect(&workerC, &WorkerC::data_c_signal, this, &MainWindow::data_c_slot);
threadA.start();
threadB.start();
threadC.start();
主要就是对象间的信号槽设置
我们现在再来看看客户分享的代码片段
显然,客户的这几个 working 函数是槽函数,
所以那几个 Worker 类(ImagePrepare、ImageDetectOne、ImageDetectTwo)的 working 都得添加 public slots: 声明
其次,QThread::started 信号是在 QThread::start 函数执行后就会发出的,一般我们不会直接用这个信号
我们会在需要的时候(比如按下按钮或者网络端接收到数据时)通过自定义的信号关联 Worker 类的槽函数
比如 connect(&workerA, &CameraClient::image_signal, &workerB, &WorkerB::image_slot);)
最后,可以直接在栈上创建 Worker 类的对象(比如我的 workerA workerB workerC workerD),
如果通过 new 的方式(比如图中的 imgPre)我们需要添加 connect 手动管理 worker 对象的销毁。
另外,如果选择堆上创建,new Worker 后不要带 this 参数
Worker *worker = new Worker; worker->moveToThread(&workerThread); connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);