Qt修炼手册11

    xiaoxiao2021-03-25  142

    1.事件循环

    学习QT多线程编程之前,有必要先熟悉事件循环的概念。 先看一个单线程界面程序的主函数代码: int main(int argc, char* argv[]) {   QApplication app(argc, argv);   // 构造主窗口对象并显示   MainWindow w;   w.show();   // 进入事件循环   return app.exec(); } 在程序初始化完成后,主线程进入main()函数开始执行应用代码。一般地,我们在主线程上构建界面对象然后进入事件循环以处理控件绘制、用户输入、系统输出等消息。这就是我们通常说的事件驱动模型。 这里需要提一个问题,为什么需要多线程编程? 多线程编程旨在提高人机交互的感受。主线程承担着用户交互的重任,当在主线程上运行费时的代码时,就会影响用户的正常操作。所以我们常把一些费时费力的计算工作移出主线程,开辟新的线程来运行之。 QThread是QT中用于线程管理的类,调用一个QThread对象的start()方法时,会创建一个新的线程并执行它的run()方法。默认地,run()会调用exec()方法进入自己的消息循环中。如下图所示: 上图中有主线程、工作线程都是执行事件循环,并且注意到主线程内部含有thr、w、objs这些QObject对象(这些对象都是在主线程上创建的)。主线程的事件循环负责检测这些对象是否有消息要处理,有的话则调用对象的slot方法。可以使用QObject::moveToThread方法将某个对象移到其他线程中,譬如: class Worker : public QObject { Q_OBJECT … } void someFunc() { QThread thr = new QThread; Worker worker = new Worker; worker->moveToThread(thr); thr->start();   … } 如果在主线程上调用someFunc(),则thr和worker在创建后关联在主线程上,当调用worker-> moveToThread()后,worker对象关联到了新的线程中,如图所示: 2.QThread类

    2.1 类基础

    QThread类可以不受平台影响而实现线程。QThread提供在程序中可以控制和管理现成的多种成员函数和信号/槽。通过QThread类的成员函数start()启动线程。 class Worker : public QObject { Q_OBJECT public slots: void doWork() { ...... } }; void MyObject::putWorkerInThread() { Worker *worker = new Worker; QThread *workerThread = new QThread(this); connect(workerThread, &QThread::started, worker, &Worker::doWork); connect(workerThread, &QThread::finished,worker, &Worker::deleteLater); worker->moveToThread(workerThread); //开始进行事件循环,并发射信号workerThread->start(); workerThread->start(); } 上述代码中,Worker类的槽启动分离线程时发送的信号线程终止时发送的信号相关联。如果我们启动了线程,就会调用Worker类的槽函数doWork()。 换言之,如果运行putWorkerInThread()的函数start(),那么就相当于发送了线程的启动信号(QThread::started),根据定义的信号-槽,程序就会调用与函数connect()相关联的Worker类的槽函数doWorker()。如果结束线程,则自动发送QThread::finished信号,发送此信号后,立即释放Worker使用过的线程内存区域。 QThread通过信号函数started()和finished()通知开始和结束,并查看线程状态。可以确认究竟是使用函数isfinished()信号终止线程,还是使用函数isRunning()启动线程。使用函数exit()和quit()可以结束线程。

    2.2 多线程初步

    如果使用多线程,有时需要等到所有线程终止。此时,使用函数wait()即可。线程中,使用成员函数sleep()、msleep()、usleep()可以暂停秒、毫秒及微秒单位的线程。

    3.QThread使用的内存区域

    QThread使用的内存区域分为线程私有区域和共享内存区域。线程内部使用的寄存器区域只能在线程内部共享。共享数据区域可以访问其他线程。 线程内部使用的寄存器和栈区域如下图所示: 线程内部共享区域虽然不能访问其他线程,但栈区域可以在线程间共享。因此,如果实现多种线程访问栈区域,需要注意互斥体,读写锁等线程的安全性。

    3.多线程编程实例与解析

    widget.h #ifndef WIDGET_H #define WIDGET_H #include <QtWidgets/QWidget> #include "ui_widget.h" #include <QThread> #include <QPushButton> #include <QMutex> class MyThread : public QThread { Q_OBJECT public: MyThread(int num); private: bool threadStop; int number; QMutex mutex; public: void stop(); protected: void run(); }; / class widget : public QWidget { Q_OBJECT public: widget(QWidget *parent = 0); ~widget(); MyThread *thread1; MyThread *thread2; private slots: void btn_start(); void btn_stop(); void btn_isRunning(); void btn_isFinished(); private: Ui::widgetClass ui; }; #endif // WIDGET_H widegt.cpp #include "widget.h" MyThread::MyThread(int num) { number = num; } void MyThread::stop() { threadStop = true; qDebug("[%d] Thread stop", number); } void MyThread::run() { threadStop = false; int i = 0; while(!threadStop) { mutex.lock(); qDebug("[%d] MyThread %d", number, i); i++; sleep(1); mutex.unlock(); } } /// widget::widget(QWidget *parent) : QWidget(parent) { ui.setupUi(this); thread1 = new MyThread(1); thread2 = new MyThread(2); QPushButton *btn_start = new QPushButton("START", this); btn_start->setGeometry(10, 10, 80, 40); QPushButton *btn_stop = new QPushButton("STOP", this); btn_stop->setGeometry(100, 10, 80, 40); QPushButton *btn_isRunning = new QPushButton("IsRunning", this); btn_isRunning->setGeometry(200, 10, 100, 40); QPushButton *btn_isFinished = new QPushButton("IsFinished", this); btn_isFinished->setGeometry(310, 10, 100, 40); connect(btn_start, SIGNAL(clicked()), this, SLOT(btn_start())); connect(btn_stop, SIGNAL(clicked()), this, SLOT(btn_stop())); connect(btn_isRunning, SIGNAL(clicked()), this, SLOT(btn_isRunning())); connect(btn_isFinished, SIGNAL(clicked()), this, SLOT(btn_isFinished())); } void widget::btn_start() { thread1->start(); thread2->start(); } void widget::btn_stop() { thread1->stop(); thread2->stop(); } void widget::btn_isRunning() { if(thread1->isRunning()) qDebug("[1] Thread is running"); else qDebug("[1] Thread is not running"); if(thread2->isRunning()) qDebug("[2] Thread is running"); else qDebug("[2] Thread is not running"); } void widget::btn_isFinished() { if(thread1->isFinished()) qDebug("[1] Thread Finish"); else qDebug("[1] Thread not Finish"); if(thread2->isFinished()) qDebug("[2] Thread Finish"); else qDebug("[2] Thread not Finish"); } widget::~widget() {} 输出结果: 代码分析: 1.QThread信号与槽 信号:终止线程实例运行发送信号(void finished)、启动线程实例发送信号(void started)、结束线程实例发送信号(void terminated)槽:线程中止运行槽(finished)、线程启动槽(start)、线程结束槽(terminate) 2.QThread优先级 QThread通过函数setPriority()设置优先级。

    4.关于QDebug的一点思考                                               

    VS2010开发Qt,怎么显示qDebug信息(添加DOS窗口):   
    转载请注明原文地址: https://ju.6miu.com/read-3022.html

    最新回复(0)