本文介绍的是理解 Qt 多线程类 起点学习,QThread 是Qt中一个对线程支持的核心的底层类。每个线程对象代表了一个运行的线程。

浅谈 Qt 多线程 起点学习是本文要介绍的内容,不多说,先来看内容。由于Qt的跨平台特性,QThread成功隐藏了所有在不同操作系统里使用线程的平台相关性代码。

浅谈 Qt 线程类 起点学习(qt线程类详解)  线程 类 第1张

POINT 1:QThread的实例与普通的实例没什么不同,只是运行着的run()函数会不同。

例1:

  1. classMThread:publicQThread
  2. {
  3. public:
  4. MThread();
  5. ~MThread();
  6. voidrun();
  7. voidfoo();
  8. ...
  9. };
  10. classMDialog:publicQDialog
  11. {
  12. ...
  13. MThread*mythread;
  14. };
  15. MDialog::MDialog()
  16. {
  17. mythread=newMThread;
  18. ...
  19. }

需要注意的是,在QT中,QThread对象的实例mythread是属于创建它的线程(线程A,即MDialog所在的线程)的,mythread的所有程序代码与数据都放在与MDialog相同的空间中.这时的mythread,就像任何普通的自己定义的类的实例一样.但是在调用mythread->start()之后,mythread的run()函数中的代码会在新的线程(线程B)中执行.在run()函数中声明的变量\实例化的对象,都属于线程B.

但是mythread的所有代码,都还在存储在线程A中,只是run()函数的"执行"是在线程B中.在MDialog中,使用mythread->foo();foo()是在线程A中执行的.在MDialog中使用connect(this, SIGNAL(sigDialogSignal()), mythread, SLOT(slotThreadSlot()));

当emit sigDialogSignal()时,是会在MDialog所在的线程A中执行的.因为mythread与MDialog同属于一个线程, 这时thread可以看做一个普通的实例.另外,因为connect函数的连接方式默认是自动连接,而对同属于一个纯种的两个对象,自动连接会使用直接连接,即slot在发出signal的线程中立即执行.

例2:

  1. #include"mthread.h"
  2. #include<QDebug>
  3. MThread::MThread(QObject*parent)
  4. :QThread(parent)
  5. {
  6. myTimer.start(1);
  7. connect(&myTimer,SIGNAL(timeout()),this,SLOT(slotPrint()));
  8. }
  9. MThread::~MThread()
  10. {
  11. }
  12. voidMThread::run()
  13. {
  14. for(inti=0;i<100;++i){
  15. for(intj=0;j<10000;++j){
  16. qDebug()<<"---------"<<i;
  17. }
  18. }
  19. exec();
  20. }
  21. voidMThread::slotPrint()
  22. {
  23. qDebug()<<"==============================";
  24. }

运行后出现:

  1. ...
  2. ...
  3. ---------9
  4. ==============================================================
  5. ---------9
  6. ...
  7. ...

不能误以为:在一个QThread类的派生类中,run()函数中的语句在运行时,可能被本线程定时器超时slot中断. (错误)

事实上,slotPrint()在创建MThread的实例的线程中执行.

POINT 2:线程B中的对象要想接收线程A中的对象发来的signal, 必须进入exec(), 如在exec()前有死循环, 没有进入exec(), 则线程B中的对象不会收到signal.

  1. voidMThread::run()
  2. {
  3. while(1){
  4. dosomething();//此循环永不退出
  5. }
  6. exec();//如果此事件循环不能进入,刚此线程不会收到任何signal
  7. }

POINT 3:线程A中的指针可指向线程B中创建的对象实例, 这个实例属于线程B. 指针仅仅是一个地址, 而对象实例的变量/代码等都属于线程B.

例1:

  1. classMThread:publicQThread
  2. {
  3. Q_OBJECT
  4. public:
  5. MThread(QObject*parent=0);
  6. ~MThread();
  7. voidrun();
  8. MPrint*mprint;
  9. };
  10. voidMThread::run()
  11. {
  12. mprint=newMPrint;
  13. exec();
  14. }
  15. //如此声明,mprint所指向的对象属于另一个线程.

例2:

  1. classMThread:publicQThread
  2. {
  3. Q_OBJECT
  4. public:
  5. MThread(QObject*parent=0);
  6. ~MThread();
  7. voidrun();
  8. MPrint*mprint;
  9. private:
  10. QTimer*myTimer;
  11. privateslots:
  12. voidslotPrint();
  13. voidtestFoo();
  14. };
  15. voidMThread::run()
  16. {
  17. myTimer=newQTimer;
  18. mprint=newMPrint;
  19. myTimer->setInterval(100);
  20. connect(myTimer,SIGNAL(timeout()),this,SLOT(testFoo()),Qt::DirectConnection);
  21. QTimer::singleShot(0,myTimer,SLOT(start()));
  22. exec();

上这样写run(),myTimer在run()中new,即myTimer这个指针属于旧线程,但myTimer所指向的QTimer实例的实体在新的线程中,testFoo()会在新线程中执行,例3:

  1. voidMThread::run()
  2. {
  3. QTimermyTimer;
  4. mprint=newMPrint;
  5. myTimer.setInterval(100);
  6. connect(&myTimer,SIGNAL(timeout()),this,SLOT(testFoo()),Qt::DirectConnection);
  7. QTimer::singleShot(0,&myTimer,SLOT(start()));
  8. //testFoo();
  9. exec();
  10. }

以上这样写run(),myTimer在run()中声明,即myTimer属于新的线程,testFoo()也会在新线程中执行,例4:

  1. classMThread:publicQThread
  2. {
  3. Q_OBJECT
  4. public:
  5. MThread(QObject*parent=0);
  6. ~MThread();
  7. voidrun();
  8. MPrint*mprint;
  9. private:
  10. QTimermyTimer;
  11. privateslots:
  12. voidslotPrint();
  13. voidtestFoo();
  14. };
  15. voidMThread::run()
  16. {
  17. mprint=newMPrint;
  18. myTimer.setInterval(100);
  19. connect(&myTimer,SIGNAL(timeout()),this,SLOT(testFoo()));
  20. QTimer::singleShot(0,&myTimer,SLOT(start()));
  21. //testFoo();
  22. exec();
  23. }

以上这样写run(),testFoo()会在创建myTimer的老线程中执行.因为可以看到,mytimer和this(即mythread),都是在同一个线程中,只是在另一个线程中(run()),做了connect操作.

要注意的是,在线程B中启动线程A中的一个定时器,不能使用myTimer.start(),这样启动不了定时器.而应使用signal来触发start()这个slot.

POINT 5:slot不会中断同线程中的slot,例1:

  1. #include"mthread.h"
  2. #include<QDebug>
  3. MThread::MThread(QObject*parent)
  4. :QThread(parent)
  5. {
  6. myTimer.start(1);
  7. connect(&myTimer,SIGNAL(timeout()),this,SLOT(slotPrint()));
  8. }
  9. MThread::~MThread()
  10. {
  11. }
  12. voidMThread::run()
  13. {
  14. exec();
  15. }
  16. voidMThread::slotPrint()
  17. {
  18. qDebug()<<"===========================";
  19. for(inti=0;i<100;++i){
  20. for(intj=0;j<10000;++j){
  21. qDebug()<<"---------"<<i;
  22. }
  23. }
  24. }

slotPrint()函数运行完之后才会退出,说明slot不会中断slot,一个slot在执行完之后才会执行下一个slot.

注意:slotPrint()在创建MThread实例的线程中执行.而不是使用thread->start()创建出的那个线程,例2:

  1. #include"mthread.h"
  2. #include<QDebug>
  3. MThread::MThread(QObject*parent)
  4. :QThread(parent)
  5. {
  6. myTimer.start(1);
  7. connect(&myTimer,SIGNAL(timeout()),this,SLOT(slotPrint()));
  8. }
  9. MThread::~MThread()
  10. {
  11. }
  12. voidMThread::run()
  13. {
  14. testFoo();
  15. exec();
  16. }
  17. voidMThread::slotPrint()
  18. {
  19. qDebug()<<"=======================";
  20. }
  21. voidMThread::testFoo()
  22. {
  23. for(inti=0;i<100;++i){
  24. for(intj=0;j<10000;++j){
  25. qDebug()<<"---------"<<i;
  26. }
  27. }
  28. }

以上代码中,slotPrint()与testFoo()会在两个不同的线程中执行.

小结:浅谈 Qt 多线程类 起点学习的内容介绍完了,希望本文对你有所帮助。更多内容请参考编辑推荐。

转载请说明出处
知优网 » 浅谈 Qt 线程类 起点学习(qt线程类详解)

发表评论

您需要后才能发表评论