提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言:应用程序在某些情况下需要处理比较复杂的逻辑,例如常规的图传上位机,如果在传输图片跑到较高码流或对图像执行一些处理任务是,引用多线程可以明显改善响应度和反馈速度。
- QT中所包含的线程类
- 多线程的使用方式
- QT线程池的使用
QT多线程使用的注意事项:
1.QT的默认线程为主线程:负责窗口事件处理或窗口控件数据的更新;
2.子线程不能对窗口对象做任何操作;
3.主线程依托信号槽机制与子线程之间进行数据传递;
提示:以下是本篇文章正文内容,下面案例可供参考
一、QT线程类:QThread
建议先过一遍 QThread Class 文档。
使用方法
1.引入库
new QThread Class & Override run()
/*------------------------------WorkerThread-----------------------------------*/ class WorkerThread : public QThread { Q_OBJECT public: explicit WorkerThread(); protected: void run(); signals: void resultReady(const QString &s); }; void WorkerThread::run(){ /* ... here is the expensive or blocking operation ... */ } /*------------------------------MainWindow-----------------------------------*/ void MainWindow::startWorkInAThread() { WorkerThread *workerThread = new WorkerThread(); // Release object in workerThread connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater); workerThread->start(); }
run()中未调用exec(),那么在run()结束后,线程自动退出;
在上述例程中,WorkThread存在于实例化它的旧线程中,仅有run()是在子线程中执行,验证方法:
qDebug()<<"mythread QThread :: currentThreadId()==" << QThread::currentThreadId()
;
该案例也说明QThread本身并不是一个线程,QThread Class 是一个线程管理器。
new Object Class & moveToThread(new Qtread)
/*------------------------------DisconnectMonitor-----------------------------------*/ class DisconnectMonitor : public QObject{ Q_OBJECT public: explicit DisconnectMonitor(); signals: void StartMonitor(long long hanlde); void StopMonitor(); void Disconnect(); private slots: void slot_StartMonitor(long long hanlde); void slot_StopMonitor(); void Monitor(); private: long long ControllerHanlde; QTimer * MonitorTimer; }; DisconnectMonitor::Disconnectmonitor() { Monitor Timer= new QTimer; ControllerHanlde = 0; connect(MonitorTimer, &Qtimer::timeout, this, &DisconnectMonitor::Monitor); connect(this, &DisconnectMonitor::StartMonitor, this, &DisconnectMonitor::slot_StartMonitor); connect(this, &DisconnectMonitor::StopMonitor, this, &DisconnectMonitor::slot_StopMonitor); MonitorTimer->start(TAKETIME); } void DisconnectMonitor::Monitor(){ if(ControllerHanlde == 0){ return; } else{ int state = IsConnect(ControllerHanlde); if(state != 0){ emit Disconnect(); } } } class Controller : public QObiect{ Q_OBJECT QThread workerThread; public: Controller(){ DisconnectMonitor *monitor = new DisconnectMonitor; monitor->moveToThread(&workerThread); connect(workerThread, &QThreads::finished, monitor, &QObject::deleteLater); connect(monitor, SIGNAL(Disconnec t()), this, SLOT(DisconnectManage())); workerThread.start(); } ~Ciontroller(){ workerThread.quit(); workerThread.wait(); } private slots: voidDisconnectManage(); };
在该例程中的DisconnectMonitor 中定义了一个定时器用以实现定时检测。
在上述例程中,WorkThread存在于实例化它的旧线程中,仅有run()是在子线程中执行,验证方法:
qDebug()<<"mythread QThread :: currentThreadId()==" << QThread::currentThreadId()
;
该案例也说明QThread本身并不是一个线程,QThread Class 是一个线程管理器。在上述案例中并不能认为monitor的控制权归属于新线程,movetoThread()的作用是将槽函数在指定的的线程中调用,仅有槽函数在指定线程中调用,包括槽函数在指定线程中调用,包括构造函数都仍在主线程调用 。
DisconnectMonitor 须继承自顶层父类Object,否则不能移动;
如果Thread 为nullptr, 则该对象机器子对象的所有事件处理都将停止(与线程不再关联);
调用moveThread()时,移动对象的所有计时器都将被重置。且它只能将一个对象“推”到另一个线程,不能将对象从任意线程推到当前线程(该对象与其他线程有关联)。
2.开启线程
代码如下(示例):使用QThread->start()开启线程
/*-----------------------qthread_win.cpp---------------------------------*/
void QThread::start(Priority priority)
{
Q_D(QThread);
QMutexLocker locker(&d->mutex);
if (d->isInFinish) {
locker.unlock();
wait();
locker.relock();
}
if (d->running)
return;
d->running = true;
d->finished = false;
d->exited = false;
d->returnCode = 0;
d->interruptionRequested = false;
// 【1】判断当前环境,调用系统API创建线程 d->handle 为线程句柄
#if defined(Q_CC_MSVC) && !defined(_DLL) // && !defined(Q_OS_WINRT)
# ifdef Q_OS_WINRT
# error "Microsoft documentation says this combination leaks memory every time a thread is started. " \
"Please change your build back to -MD/-MDd or, if you understand this issue and want to continue, " \
"edit this source file."
# endif
// MSVC -MT or -MTd build
d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
this, CREATE_SUSPENDED, &(d->id));
#else
// MSVC -MD or -MDd or MinGW build
d->handle = CreateThread(nullptr, d->stackSize,
reinterpret_cast<LPTHREAD_START_ROUTINE>(QThreadPrivate::start),
this, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&d->id));
#endif // Q_OS_WINRT
//创建线程失败
if (!d->handle) {
qErrnoWarning("QThread::start: Failed to create thread");
d->running = false;
d->finished = true;
return;
}
//优先级
int prio;
d->priority = priority;
switch (d->priority) {
case IdlePriority:
prio = THREAD_PRIORITY_IDLE;
break;
case LowestPriority:
prio = THREAD_PRIORITY_LOWEST;
break;
case LowPriority:
prio = THREAD_PRIORITY_BELOW_NORMAL;
break;
case NormalPriority:
prio = THREAD_PRIORITY_NORMAL;
break;
case HighPriority:
prio = THREAD_PRIORITY_ABOVE_NORMAL;
break;
case HighestPriority:
prio = THREAD_PRIORITY_HIGHEST;
break;
case TimeCriticalPriority:
prio = THREAD_PRIORITY_TIME_CRITICAL;
break;
case InheritPriority:
default:
prio = GetThreadPriority(GetCurrentThread());
break;
}
if (!SetThreadPriority(d->handle, prio)) {
qErrnoWarning("QThread::start: Failed to set thread priority");
}
if (ResumeThread(d->handle) == (DWORD) -1) {
qErrnoWarning("QThread::start: Failed to resume new thread");
}
}
3.退出线程
首先,删除QThread对象并不会停止起管理的线程执行。删除正在运行的QThread将导致程序崩溃,在删除之前我们须等待finish信号。
1.对于未开启事件循环的线程,我们仅需让run()执行结束即可终止线程。
void TestThread::stopThread(){
mutex.lock();
runenable = false;
mutex.unlock();
}
void TestThread::run(){
runenable = true;
while(1){
if(mutex.tryLock()){
if(!runenable)
break;
}
}
}
2.对于开启事件循环的线程,我们应该操作的是退出事件循环。
- 若线程开启了EvenLoop,我们可调用 quit()/exit() + wait() 实现退出。
- 调用terminate()后,由于线程可能在任何位置中值,强制结束线程很危险:可能修改数据、可能导致线程状态无法清除、可能导致锁异常。因此终止后仍须使用wait()。
- 以上手段军存在内存泄漏的情况,我们可以使用finish信号。
二、线程优先级的设定
在QThread中我们可以通过setPriority()设置线程优先级,通过priority()获取线程优先级。
QThread还提供了sleep()\msleep()\usleep()静态函数,进行短暂的线程屏蔽。
三、QT线程类:QThread
在大多情况下,线程必须停止以等待其他线程。
使用方法
1.互斥锁
QMutex 任意时刻至多有一个线程可以使用该锁(常用于数据共享)
QMutex mutex;
void thread1(){
mutex.lock();
mutex.unlock();
}
2.读写锁
QReadWriteLock 允许多个线程对数据共享进行读取,可提高多线程程序的并发度。
void ReadThread::run(){
lock.lockForRead();
read_file();
lock.unlock();
}
void ReadThread::run(){
lock.lockForWrite();
write_file();
lock.unlock();
}
四、线程合理开启
线程数量=可用核心数量/(1 - 阻塞系数)
最简单的办法是 cpu核心数*2+2