QT多线程使用moveToThread

使用线程有两种方法:

一、平时我们使用线程的时候一般是继承QThread,实现它的run()函数,将需要在线程执行的代码放在run()里边运行。如果需要在线程

 
  1. {

  2.  
  3. while(bRun)//如果需要退出线程就将bRun设置为false.

  4. {

  5. qDebug()<<"run thread ID = "<<QThread::currentThreadId();

  6. QThread::usleep(0);//usleep的值设置为0一样会占满cpu,但是根据老大测试,并不会使程序变卡。你也可以设置为10.

  7. }

  8. // this->exec();//没加这个的话线程就会结束,并发出Finsh()信号

  9. }

如果使用这一方法,QThread::quit()没有效果。因为这个线程根本就不需要事件循环。这种情况想退出,将bRun设置为false或者直接使用QT很不推荐的terminate().

 

二、使用moveToThread(),因为在Qt4.3(包括)之前,run 是虚函数,必须子类化QThread来实现run函数。而从Qt4.4开始run() 默认调用 QThread::exec() ,线程在调用quit()、exit()或terminat()之前不会退出。这样一来不需要子类化 QThread 了,只需要子类化一个 QObject 就够了,这正是被 Bradley T. Hughes(Qt的开发人员)推荐的方法。怎么用呢,使用connect()!!所以下边要了解信号和槽的关系。等下会分析下connnect的第五个参数。对了,如果moveToThread里执行的函数没执行完,你是无法通过quit来结束的,必须使用第一种方法:最歹毒的一招mthread->terminate()强制退出。

 

 
  1. class MyMoveToThreadFunc :public QObject

  2. {

  3. Q_OBJECT

  4. public:

  5. void showObjectThreadID()

  6. {

  7. qDebug()<<"# MyMoveToThreadFunc thread id = "<<QThread::currentThreadId();

  8. }

  9. signals:

  10. void again();

  11. public slots:

  12. void slotOfThread()

  13. {

  14. qDebug()<<" MyMoveToThreadFunc slot thread id = "<<QThread::currentThreadId();

  15. emit again();

  16. }

  17. };

  18.  
  19. class MyTry:public QObject

  20. {

  21. Q_OBJECT

  22. public:

  23. MyTry();

  24. QThread *mThread;

  25. MyMoveToThreadFunc *mFunc;

  26. };

 

 
  1. MyTry::MyTry()

  2. {

  3. mThread = new QThread();

  4. qDebug()<<"main thread id = "<<QThread::currentThreadId();

  5. mFunc = new MyMoveToThreadFunc();

  6. mFunc->moveToThread(mThread);</span>

  7. QObject::connect(mThread,SIGNAL(started()),mFunc,SLOT(slotOfThread()));//slot将会在mThread中运行

  8. mFunc->showObjectThreadID()//在主程序运行。

  9. mThread->start();//启动线程

  10. }

 

如果要实现事件循环怎么办?一样,随便找个信号连接到slotOfThread(),循环发送就行了。例如,你重载mThread,让他以第一种循环循环发出信号给mFunc连接也行。记住,直接在主程序调用mFunc的函数,函数还是会是在主程序运行。在这啰嗦一句,子类化QThread的子类,只有在run()函数里才是属于线程。所以有时候你在子类构造函数创建的实例是不属于线程创建的,有时候就会提示这种: QObject::startTimer: Timers can only be used with threads started with QThread 。(我还没测试,别人发的问题认为是这个)。

 

现在我们来分析下connect()的五个参数。先吃饭,可能明天写完。

有六种参数:

1

2

3

4

5

6

Qt::AutoConnection

Qt::DirectConnection

Qt::QueuedConnection

Qt::BlockingQueuedConnection

Qt::UniqueConnection

Qt::AutoCompatConnection

这里面一共有六种方式。

前两种比较相似,都是同一线程之间连接的方式,不同的是Qt::AutoConnection是系统默认的连接方式。这种方式连接的时候,槽不是马上被执行的,而是进入一个消息队列,待到何时执行就不是我们可以知道的了,当信号和槽不是同个线程,会使用第三种QT::QueueConnection的链接方式。如果信号和槽是同个线程,调用第二种Qt::DirectConnection链接方式。

第二种Qt::DirectConnection是直接连接,也就是只要信号发出直接就到槽去执行,无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行,一旦使用这种连接,槽将会不在线程执行!。

第三种Qt::QueuedConnection和第四种Qt::BlockingQueuedConnection是相似的,都是可以在不同进程之间进行连接的,不同的是,这里第三种是在对象的当前线程中执行,并且是按照队列顺序执行。当当前线程停止,就会等待下一次启动线程时再按队列顺序执行  ,等待QApplication::exec()或者线程的QThread::exec()才执行相应的槽,就是说:当控制权回到接受者所依附线程的事件循环时,槽函数被调用,而且槽函数在接收者所依附线程执行,使用这种连接,槽会在线程执行。

第四种Qt::BlockingQueuedConnection是(必须信号和曹在不同线程中,否则直接产生死锁)这个是完全同步队列只有槽线程执行完才会返回,否则发送线程也会等待,相当于是不同的线程可以同步起来执行。

第五种Qt::UniqueConnection跟默认工作方式相同,只是不能重复连接相同的信号和槽;因为如果重复链接就会导致一个信号发出,对应槽函数就会执行多次。

第六种Qt::AutoCompatConnection是为了连接QT4 到QT3的信号槽机制兼容方式,工作方式跟Qt::AutoConnection一样。显然这里我们应该选择第三种方式,我们不希望子线程没结束主线程还要等,我们只是希望利用这个空闲时间去干别的事情,当子线程执行完了,只要发消息给主线程就行了,到时候主线程会去响应。

 

后记

 

为什么要使用moveToTread()呢。

eg:moveToThread对比传统子类化Qthread更灵活,仅需要把你想要执行的代码放到槽,movetothread这个object到线程,然后拿一个信号连接到这个槽就可以让这个槽函数在线程里执行。可以说,movetothread给我们编写代码提供了新的思路,当然不是说子类化qthread不好,只是你应该知道还有这种方式去调用线程。

老大认为,轻量级的函数可以用movethread,多个短小精悍能返回快速的线程函数适用 ,无需创建独立线程类,例如你有20个小函数要在线程内做, 全部扔给一个QThread。而我觉得movetothread和子类化QThread的区别不大,更可能是使用习惯引导。又或者你一开始没使用线程,但是后边发觉这些代码还是放线程比较好,如果用子类化QThread的方法重新设计代码,将会有可能让你把这一段推到重来,这个时候,moveThread的好处就来了,你可以把这段代码的从属着movetothread,把代码移到槽函数,用信号触发它就行了。其它的话movetothread它的效果和子类化QThread的效果是一样的,槽就相当于你的run()函数,你往run()里塞什么代码,就可以往槽里塞什么代码,子类化QThread的线程只可以有一个入口就是run(),而movetothread就有很多触发的入口。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt中的多线程可以使用`QThread`类来实现,而`moveToThread`是一个很常用的函数,用于将一个QObject对象移动到另一个线程中执行。 下面是一个使用`moveToThread`的简单例子: ```cpp #include <QCoreApplication> #include <QThread> #include <QDebug> class Worker : public QObject { Q_OBJECT public: Worker(QObject *parent = nullptr) : QObject(parent) {} public slots: void doWork() { qDebug() << "Worker thread:" << QThread::currentThreadId(); } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Worker worker; QThread thread; worker.moveToThread(&thread); QObject::connect(&thread, &QThread::started, &worker, &Worker::doWork); QObject::connect(&worker, &Worker::destroyed, &thread, &QThread::quit); thread.start(); return a.exec(); } ``` 在上面的例子中,我们创建了一个`Worker`类,它继承自`QObject`类,并有一个`doWork`槽函数。我们将`worker`对象通过`moveToThread`函数移动到了`thread`线程中。然后,我们通过`connect`函数将`thread`的`started`信号连接到`worker`的`doWork`槽函数上,当线程启动时,`doWork`槽函数会在`thread`线程中执行。同时,我们还将`worker`的`destroyed`信号连接到`thread`的`quit`槽函数上,以保证线程能够正确退出。 需要注意的是,如果我们将一个QObject对象移动到了另一个线程中执行,那么它的所有信号和槽函数都必须在该线程中执行,否则会出现问题。所以,在上面的例子中,我们将`worker`对象的`doWork`槽函数定义为`public slots`,并且在`thread`线程中执行,以保证它能正确执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值