1、用QObject的方法实现多线程
Qt有两种多线程的方法,其中一种是继承QThread的run函数,另外一种是把一个继承于QObject的类转移到一个Thread里。 Qt4.8之前都是使用继承QThread的run这种方法,但是Qt4.8之后,Qt官方建议使用第二种方法。
第二种方法用QObject来实现多线程有个非常好的优点,就是默认就支持事件循环(Qt的许多非GUI类也需要事件循环支持,如QTimer、QTcpSocket),QThread要支持事件循环需要在QThread::run()中调用QThread::exec()来提供对消息循环的支持,否则那些需要事件循环支持的类都不能正常发送信号,因此如果要使用信号和槽,那就直接使用QObject来实现多线程。
https://doc.qt.io/qt-5/threads.html
https://wiki.qt.io/QThreads_general_usage
https://wiki.qt.io/Threads_Events_QObjects
2、用QtConcurrent::run的方法实现多线程
https://doc.qt.io/qt-5/qtconcurrentrun.html
https://www.coologic.cn/2017/12/608/
2.1 基本的使用方式,注意线程id的获取方式是QThread::currentThreadId();
QString id = QString::asprintf("[%d]", QThread::currentThreadId());
#include <QCoreApplication> #include <QDebug> #include <QList> #include <QThread> #include <QtConcurrent> void function(const QList<int> ¶m1, const int ¶m2, Qt::HANDLE main_id) { qDebug() << "function param:" << param1 << param2 << main_id; qDebug() << "function thread id:" << QThread::currentThreadId(); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QList<int> testVactor; for (int i = 1; i <= 3; i++) { testVactor.push_back(i); } qDebug() << "main thread id:" << QThread::currentThreadId(); QFuture<void> f = QtConcurrent::run(function, testVactor, 666, QThread::currentThreadId()); f.waitForFinished(); //要等待,否则线程没运行完程序结束会出错 return 0; }
2.2 指定线程池的使用方式
有时候希望运行的函数在全局线程池或者局部线程池运行,而不是有qt托管处理,可以进行如下方式调用:
extern void aFunction();
QThreadPool pool;
QFuture<void> future = QtConcurrent::run(&pool, aFunction);
QtConcurrent::run和线程池组合使用的源码案例:
#include <QCoreApplication> // Qt includes #include <QtConcurrent> #include <QtCore> #include <QtGui> #include <QtNetwork> #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0)) #include <QtWidgets> #endif QThreadPool m_threadpool; void func(void) { qDebug() << "hello"; QFuture<void> f4 = QtConcurrent::run(&m_threadpool, [&](void) { for (int i = 1000; i < 1010; i++) { qDebug() << i; Sleep(1000); } }); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); //初始化线程池 int cpu = std::thread::hardware_concurrency(); qDebug() << "cpu:" << cpu; m_threadpool.setMaxThreadCount(cpu); m_threadpool.setExpiryTimeout(-1); QFuture<void> f1 = QtConcurrent::run(&m_threadpool, [&](void) { for (int i = 0; i < 10; i++) { qDebug() << i; Sleep(1000); } }); QFuture<void> f2 = QtConcurrent::run(&m_threadpool, [&](void) { for (int i = 100; i < 110; i++) { qDebug() << i; Sleep(1000); } }); QFuture<void> f3 = QtConcurrent::run(func); while (true) { ; } return a.exec(); }
2.3 使用QFutureWatcher实现进度条的例子
https://wiki.qt.io/Progress_Bar
\Qt5.12.9\Examples\Qt-5.12.9\qtconcurrent\progressdialog
长时间进行操作时,通常需要显示进度条。 在某些耗时的运算中,我们没有方法来实时跟踪操作的进度,所知的只是运算完成时。 为了实现进度条,您可以使用progressBar小部件,然后在另一个线程中运行该操作(使用moveToThread)。 这通常需要创建一个特殊的对象(QObject的子类,该对象运行操作,然后发出finish信号),如果您需要对许多不同的操作执行此操作,则可能会很麻烦。但是,使用QFutureWatcher和QtConcurrent::run(),这非常容易。 这篇官方文档演示如何将这种技术与QProgressDialog和QProgressBar一起使用。
如果QProgressBar最小值和最大值都设置为0,进度条会显示一个繁忙指示,而不会显示当前的值。
3、线程池
3.1 全局线程池
QThreadPool提供了一个静态成员函数,QThreadPool * globalInstance(),使用此方法可获取一个当前进程的全局线程池,可在多个类*同使用一个线程池。
3.2 局部线程池
和常规类的使用相同,可以通过QThreadPool pool;类的实例化方式建立一个局部线程池,并由当前类维护,可保证此线程池仅供当前类应用。
3.3 QThreadPool启动的线程,需要从QRunnable类继承。需要实现QRunnable类的run虚函数。QRunnable的autoDelete默认返回true,若需要更改需要调用setAutoDelete进行更改。QRunnable只有run、autodelete、setautodelete这三个关键函数。
3.4 主要的几个函数
int activeThreadCount() const //当前的活动线程数量
void clear()//清除所有当前排队但未开始运行的任务
int expiryTimeout() const//线程长时间未使用将会自动退出节约资源,此函数返回等待时间。默认值30000ms。
int maxThreadCount() const//线程池可维护的最大线程数量。默认值CPU核心数*2。
void setExpiryTimeout(int expiryTimeout)//设置线程回收的等待时间;可以设置为-1,没有超时限制。
void setMaxThreadCount(int maxThreadCount)//设置最大线程数量
void start(QRunnable *runnable, int priority = 0)//加入一个运算到队列,注意start不一定立刻启动,只是插入到队列,排到了才会开始运行。需要传入QRunnable,priority是线程优先级。
bool waitForDone(int msecs = -1)//等待所有线程运行结束并退出,参数为等待时间,-1表示一直等待到最后一个线程退出
https://doc.qt.io/qt-5/qthreadpool.html
https://doc.qt.io/qt-5/qrunnable.html
https://www.cnblogs.com/techiel/p/8018379.html