目录
1 .QThread重要信号和函数
By default, run() starts the event loop by calling exec() and runs a Qt event loop inside the thread.
1.1 常用共用成员函数
1.2信号和槽函数
1.3静态函数
1.4 任务处理函数
2.关于QThread的依附问题:
QThread是用来管理现成的,他所依附的线程和他所管理的线程不是一个东西。
QThread所依附的线程指的是创建他的线程,即执行QThread t(0)的线程,即主线程。
QThread所管理的线程是run启动的线程;
因此一般情况下,QThread或继承的MyThread中的普通函数或槽函数(非run)都是依赖对象t从而在主线程中执行;而run则不一样;
MoveToThread
3.关于connect连接
自动连接(AutoConnection),默认的连接方式。
如果信号与槽,也就是发送者与接受者在同一线程,等同于直接连接;
如果发送者与接受者处在不同线程,等同于队列连接。
直接连接(DirectConnection)
当信号发射时,槽函数立即直接调用。无论槽函数所属对象在哪个线程,槽函数总在发送者所在线程执行。
队列连接(QueuedConnection)
当控制权回到接受者所在线程的事件循环时,槽函数被调用。槽函数在接受者所在线程执行。
阻塞连接(QBlockingQueuedConnection)
Same as Qt::QueuedConnection, except that the signalling thread blocks until the slot returns.
4.QThread的使用
用法1:将工作对象和线程对象分离,通过信号和槽函数进行通信触发。
class AsyncObject : public QObject
{
Q_OBJECT
public:
AsyncObject() = default;
~AsyncObject() = default;
public slots:
void slHandleNotify(int, const QString&);
};
void UploadManager::StartServer()
{
if (!m_bStarted)
{
m_asyncObject = new AsyncObject();
m_pTimerThread = new QThread();
m_asyncObject->moveToThread(m_pTimerThread);//所有权交付线程
connect(this, &UploadManager::sgNotifyReceived, m_asyncObject, &AsyncObject::slHandleNotify);
//默认 AutoConnection this与m_asyncObject不同线程QueuedConnection,
//在子线程中运行槽函数 且主线程不等待 Qt::BlockingQueuedConnection则同步等待
m_pTimerThread->start();
m_bStarted = true;
}
}
用法2:继承QThread,重写run函数。
这种在程序中添加子线程的方式是非常简单的,但是也有弊端,假设要在一个子线程中处理多个任务,所有的处理逻辑都需要写到run()函数中,这样该函数中的处理逻辑就会变得非常混乱,不太容易维护。
class SearchRecordThread:QThread
{
QOBJECT
public:
SearchRecordThread(Qbject*parent = nullptr);
protected:
void run() override;
}
SearchRecordThread::SearchRecordThread(Qbject*parent):QThread(parent)
{}
void SearchRecordThread::run()
{
//子线程中耗时的数据库搜索业务
}
void testThread::test()
{
SearchRecordThread* searchRecordThread= new SearchRecordThread;
searchRecordThread->start();
}
5.线程池QThreadPool
5.1. 线程池的原理
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,如果频繁创建线程和销毁线程需要时间,大大降低系统的效率。
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件), 则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
5.2.QRunable类
The QRunnable class is the base class for all runnable objects. 即QThread也是继承QRunable,因此用法一致重写run函数即可。
在 Qt 中使用线程池需要先创建任务,添加到线程池中的每一个任务都需要是一个 QRunnable 类型,因此在程序中需要创建子类继承 QRunnable 这个类,然后重写 run() 方法,在这个函数中编写要在线程池中执行的任务,并将这个子类对象传递给线程池,这样任务就可以被线程池中的某个工作的线程处理掉了。
//继承QRunnable 任务重写在run()里面
class QueryRemoteControlRunnable : public QRunnable
{
public:
QueryRemoteControlRunnable() = default;
QueryRemoteParam qrp;
void run();
};
//主线程中将该对象扔到线程池,会分配空闲的线程
void RequstRunables::queryRemoteControl(const QueryRemoteParam& qrp)
{
QueryRemoteControlRunnable* pRun = new(std::nothrow)QueryRemoteControlRunnable();
if (!pRun)
{
cLogger("Runnable")->error() << __FUNCTION__ << "从堆中创建QueryRemoteControlRunnable失败";
return;
}
pRun->qrp = qrp;
QThreadPool::globalInstance()->start(pRun);
}
5.3. QThreadPool
Qt 中的 QThreadPool 类管理了一组 QThreads, 里边还维护了一个任务队列。QThreadPool 管理和回收各个 QThread 对象,以帮助减少使用线程的程序中的线程创建成本。每个Qt应用程序都有一个全局 QThreadPool 对象,可以通过调用 globalInstance() 来访问它。也可以单独创建一个 QThreadPool 对象使用。
一般情况下,我们不需要在 Qt 程序中创建线程池对象,直接使用 Qt 为每个应用程序提供的线程池全局对象即可。得到线程池对象之后,调用 start() 方法就可以将一个任务添加到线程池中,这个任务就可以被线程池内部的线程池处理掉了,使用线程池比自己创建线程的这种多种多线程方式更加简单和易于维护 .
void MyThreadPool::InitMyThreadPool()
{
if (!m_MyThreadPool)
{
QMutexLocker Lock(&m_MyPoolMutex);
if (!m_MyThreadPool)
{
m_MyThreadPool = new QThreadPool;//创建线程池
if (m_MyThreadPool)
{
m_MyThreadPool->setMaxThreadCount(4);
}
}
}
}
QThreadPool *MyThreadPool::GetMyThreadPool()
{
if (!m_MyThreadPool)
{
InitMyThreadPool();
}
return m_MyThreadPool;
}
void MyThreadPool::DestoryThreadPool()
{
if (m_MyThreadPool)
{
m_MyThreadPool->waitForDone();
}
}
6 QtConcurrent
QtConcurrent命名空间中定义了高级线程API,避免使用低级原生线程,更加简化。
使用:包含Concurrent模块
QFuture<T> QtConcurrent::run(Function function, ...)
Equivalent to
QtConcurrent::run(QThreadPool::globalInstance(), function, ...);
/*
Runs function in a separate thread. The thread is taken from the global QThreadPool. Note that function may not run immediately; function will only be run once a thread becomes available.
T is the same type as the return value of function. Non-void return values can be accessed via the QFuture::result() function.
Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting. The QFuture returned can only be used to query for the running/finished status and the return value of the function.
See also Concurrent Run.
*/
QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)
/*
Runs function in a separate thread. The thread is taken from the QThreadPool pool. Note that function may not run immediately; function will only be run once a thread becomes available.
T is the same type as the return value of function. Non-void return values can be accessed via the QFuture::result() function.
Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting. The QFuture returned can only be used to query for the running/finished status and the return value of the function.
*/
QtConcurrent::run(m_notifyThreadPool, [=]() {
/*
working
*/
});
QPixmap QPixmapLoader::loadRemotePixmap(const QString &strUniqueId, int picType,const QString& strClientHash,const QString& strPicPath)
{
auto &&pixmap = loadBufferedRemotePixmap(strUniqueId, picType);
if (pixmap.isNull() && !strUniqueId.isEmpty()) {
QtConcurrent::run(&m_threadPool, std::bind(&QPixmapLoader::loadPixmapFromRemote, this, strUniqueId, picType, strClientHash, strPicPath));
}
return pixmap;
}
#include<QThread>
#include<QtConcurrent>
void Test{
QtConcurrent::run([]{
std::cout << QThread::currentThread() << std::endl;
});
QtConcurrent::run([]{
std::cout << QThread::currentThread() << std::endl;
});
QThread::msleep(100);
QByteArray str = "Hello,World,Welcome,to,WestWorld";
QFuture<QList<QByteArray>>future = QtConcurrent::run(str, &QByteArray::split, ',');
QList<QByteArray> result = future.result();
for (auto iter : result)
{
qDebug() << iter;
}
}
7.QTimer
多线程中使用
In multithreaded applications, you can use QTimer in any thread that has an event loop. To start an event loop from a non-GUI thread, use QThread::exec(). Qt uses the timer's thread affinity to determine which thread will emit the timeout() signal. Because of this, you must start and stop the timer in its thread; it is not possible to start a timer from another thread.
//.h
class MyTimerThread : public QThread
{
Q_OBJECT
public:
MyTimerThread(QObject *parent=nullptr);
~MyTimerThread();
void addTimer(QTimer* timer);
protected:
virtual void run();
private:
QVector<QTimer*> m_timerList;
};
//cpp
class MyTimerThread : public QThread
{
Q_OBJECT
public:
MyTimerThread(QObject *parent=nullptr);
~MyTimerThread();
void addTimer(QTimer* timer);
protected:
virtual void run();
private:
QVector<QTimer*> m_timerList;
};
MyTimerThread::MyTimerThread(QObject *parent)
{
}
MyTimerThread::~MyTimerThread()
{
quit();
wait();
}
void MyTimerThread::addTimer(QTimer* timer)
{
if (nullptr == timer)
{
return;
}
//timer所属权都交给子线程
timer->moveToThread(this);
m_timerList.append(timer);
}
void MyTimerThread::run()
{
for (auto pTimer : m_timerList) {
pTimer->start();
}
exec();//子线程里面使用QTimer需要事件循环
for (auto pTimer : m_timerList) {
pTimer->stop();
delete pTimer;
}
}
//使用测试
int Server::startServer()
{
m_pTimerThread = new MyTimerThread();
m_timerHeartBeat = new QTimer();
m_timerHeartBeat->setInterval(3000);
m_pTimerThread->addTimer(m_timerHeartBeat);//子线程中启动 Qt::DirectConnection 子线程中心跳
connect(m_timerHeartBeat, &QTimer::timeout, this, &Server::HeartBeat, Qt::DirectConnection);
//...
}
class DevManager : public QObject
{
Q_OBJECT
public:
bool startServer();
//...
public slots:
void uploadParkSlotToDev();
private:
DevManager(QObject *parent);
QTimer m_timerParksLot;
QThread *m_ParksLotThread = nullptr;
};
bool DevManager::startServer()
{
if (!m_bStarted)
{
int IntervalTime = 3000;
m_timerParksLot.setInterval(IntervalTime);
m_ParksLotThread = new QThread(this);
m_timerParksLot.moveToThread(m_ParksLotThread);//m_timerParksLot所有权交给m_ParksLotThread
connect(m_ParksLotThread, &QThread::started, &m_timerParksLot, static_cast<void (QTimer::*)()> (&QTimer::start));//主线程statred触发
connect(&m_timerParksLot, &QTimer::timeout, this, &DevManager::uploadParkSlotToETC, Qt::DirectConnection);//子线程对象发送的信号 DirectConnection连接 发送者线程(子)执行
m_ParksLotThread->start();
m_bStarted = true;
}
return m_bStarted;
}
8.QElapsedTimer
单调时钟,主要记录两个事件之间的时长(准),无法转换为人类可以读取的形式,QTimer非单调时钟,精度与操作系统有关。