这个博客老师讲得好好
Qt中多线程的使用 | 爱编程的大丙 (subingwen.cn)
Qt实现多线程编程有两种方法:
(一)继承QThread线程类
重写QThread类中的虚函数QThread::run()实现
(二)继承QObject类
在我们创建的新类中定义需要在子线程执行的任务函数,在主线程中新建任务类对象与QThread类子线程对象,并通过QObject类提供的moveToThread()方法(画重点!后边要考)将任务类对象移入子线程中。start()启动子线程,并在主线程中通过信号槽机制调用任务类中的任务函数。
/* 子线程检测人脸 */
//faceDetect = new FaceDetect(mutex, this); //PS:指定了父对象的线程,无法实现任务的移入
faceDetect = new FaceDetect(mutex);
thread = new QThread(this);
faceDetect->moveToThread(thread);
thread->start();
connect(this, &AttendanceClient::sendCamImage, faceDetect, &FaceDetect::detect);
注意:
由于Qt的对象树机制,父对象析构时,会先释放其子对象的资源,所以我们在窗口主线程定义类对象时,习惯指定其父对象(这里的父对象和父类不同),以期自动释放资源。但是,不能为将要移入子线程的类对象指定父对象!因为将任务类对象移入子线程的操作是从任务类的父类QObject类中继承的,若为任务对象指定父对象,则会出现以下警告,并在主线程中执行任务函数。
警告:
QObject::moveToThread: Cannot move objects with a parent
我在调试代码的时候,就因为打印的日志信息,忽略了这个警告,导致任务函数执行在主线程中,又因为我在子线程和主线程中均需访问主线程中的一个共享资源,来表示当前检测的状态,故我在子线程与主线程涉及的代码上下文处加了QMutex互斥锁(当然也可以用信号槽机制),结果显而易见了,由于子线程移入失败,我实际是在主线程中加了两把锁,死锁成立,程序阻塞崩溃- -。
贴士:
1. 同线程、跨线程中值传递、引用传递、指针传递区别
- 在一个线程中,Qt信号槽通信形参值传递、引用传递有区别。值传递会复制对象,引用传递不会;
- 在不同线程中,利用Qt信号槽跨线程通信时(默认QueuedConnection),值传递、引用传递均会复制对象。
所以这里子线程跨线程访问主线程栈区变量,需要用指针传递。
2.利用信号槽机制跨线程通信,需要自定义类型注册
// 在Qt信号槽中使用自定义类型,需要注意使用qRegisterMetaType对自定义类型进行注册(尤其在跨线程通信中)
qRegisterMetaType<Mat>("Mat");
qRegisterMetaType<cv::Mat>("cv::Mat");
qRegisterMetaType<int>("int*");
qRegisterMetaType<vector<cv::Rect>>("vector<cv::Rect>");