zoukankan      html  css  js  c++  java
  • 多线程中的信号与槽(中)

    令人不解的问题:

    当槽函数是线程类的成员时,为什么依然不在本线程内被调用执行?

    隐藏的问题:
    对象依附于哪一个线程?
    对象的依附性与槽函数执行的关系?
    对象的依附性是否可以改变?

    对象依附于哪个线程?
    默认情况下,对象依附于自身被创建的线程
    例如:对象在主线程(main()函数)中被创建,则依附于主线程

    int main(int argc, char* argv[])
    {
       //...
       TestThread t;     //依附于主线程
       MyObject m;       //依附于主线程
    }

    对象的依附性与槽函数执行的关系?
    默认情况下,槽函数在其所依附的线程中被调用执行

    int main(int argc, char* argv[])
    {
       //...
       TestThread t;     //依附于主线程
       MyObject m;       //依附于主线程
       
       //下面连接中的槽函数都在主线程中被调用执行
       QObject::connect(&t,SIGNAL(started()),&m,SLOT(getStarted()));
       QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot()));
    }

    对象的依附性是否可以改变?
    QObject::moveToThread用于改变对象的线程的依附性,使得对象的槽函数在依附的线程中被调用执行

    int main(int argc, char* argv[])
    {
       //...
       TestThread t;     //依附于主线程
       MyObject m;       //依附于主线程
       
       //改变对象m的线程的依附性,使其依附于线程t
       m.moveToThread(&t)
    }

    对象的依附性

    MyObject.h

    #ifndef MYOBJECT_H
    #define MYOBJECT_H
    #include <QObject>
    
    
    class MyObject : public QObject
    {
        Q_OBJECT
    public:
        MyObject();
    protected slots:
        void getStarted();
        void testSlot();
    };
    
    #endif // MYOBJECT_H
    View Code

    TestThread.h

    #ifndef TESTTHREAD_H
    #define TESTTHREAD_H
    
    #include <QThread>
    
    class TestThread : public QThread
    {
        Q_OBJECT
    protected:
        void run();
    public:
        TestThread();
    
    signals:
        void testSignal();
    protected slots:
        void testSlot();
    };
    
    #endif // TESTTHREAD_H
    View Code

    MyObject.cpp

    #include "MyObject.h"
    #include <QObject>
    #include <QThread>
    #include <QDebug>
    
    MyObject::MyObject()
    {
    
    }
    
    void MyObject::getStarted()
    {
        qDebug() <<"void MyObject::getStarted()tid = " << QThread::currentThreadId() ;
    }
    
    void MyObject::testSlot()
    {
        qDebug() << "void MyObject::testSlot() " << QThread::currentThreadId() ;
    }
    View Code

    TestThread.cpp

    #include "TestThread.h"
    #include <QDebug>
    
    TestThread::TestThread()
    {
        connect(this,SIGNAL(testSignal()),this,SLOT(testSlot()));
    }
    
    void TestThread::run()
    {
       qDebug() << "void TestThread::run() begin...tid = " << currentThreadId();
    
       for(int i=0; i<10; i++)
       {
           qDebug() << "void TestThread::run() i = " << i;
           sleep(1);
       }
    
       emit testSignal(); //发射的信号谁来接收呢,可以在构造函数中将信号和槽函数进行关联。
    
       qDebug() << "void TestThread::run() end...";
    }
    
    void TestThread::testSlot()
    {
        qDebug() << "void TestThread::testSlot() tid = " << currentThreadId();
    }
    View Code

    main.cpp

    #include <QCoreApplication>
    #include <QThread>
    #include <QDebug>
    #include "TestThread.h"
    #include "MyObject.h"
    
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        qDebug() << "main tid() " << QThread::currentThreadId();
    
        TestThread t;
        MyObject m;
    
        QObject::connect(&t,SIGNAL(started()),&m,SLOT(getStarted()));
        QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot()));
    
        m.moveToThread(&t);
    
        t.start();
        return a.exec();
    }
    View Code

    课程中使用的是Qt4,MyObject相关的槽函数没有被调用,但是使用Qt5.4,MyObject相关的槽函数被调用了。

    下面分析没有被调用的情况:

    问题:
    实验中对象m的槽函数为什么没有全部被执行?

    线程中的事件循环
    信号与槽的机制需要事件循环的支持
    QThread类中提供的exec()函数用于开启线程的事件循环
    只有开启事件循环,槽函数才能在信号发送后被调用

    信号的发送是随时随地都可以完成的,发送完成后,信号就到事件队列中去了。信号进入事件队列中去有什么用呢?
    没人理它,它什么用都没有。如何处理事件队列呢?此时事件循环就派上用场了。
    但凡通过exec开启了事件循环,就会不停的从事件队列中取信号,取到信号后就会去判断该信号有没有关联相关的槽函数,
    如果有对应的槽函数,则调用相应的槽函数。

    想要槽函数在指定的线程中被调用,需要在指定的线程中调用exec函数,开启事件循环。
    小结论:
    前提条件
    对象依附的线程开启了事件循环
    后置结果
    对象中的槽函数在依附的线程中被调用执行

    只需要在TestThread.cpp的run函数中加上exec函数即可

    void TestThread::run()
    {
       qDebug() << "void TestThread::run() begin...tid = " << currentThreadId();
    
       for(int i=0; i<10; i++)
       {
           qDebug() << "void TestThread::run() i = " << i;
           sleep(1);
       }
    
       emit testSignal(); //发射的信号谁来接收呢,可以在构造函数中将信号和槽函数进行关联。
    
       exec();
       qDebug() << "void TestThread::run() end...";
    }
    View Code

     从打印结果看,与上面没有使用exec的执行结果并无不同,很可能是因为版本不同造成的。

    从打印结果看,MyObject的两个槽函数都被调用了,且是在依附于t的那个线程。但是t的槽函数还是依附于主线程,我也想让t的槽函数依附t,怎么操作?非常简单,只需要在main.cpp中的main函数中,加入t.moveToThread(&t)即可,如下所示:

    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        qDebug() << "main tid() " << QThread::currentThreadId();
    
        TestThread t;
        MyObject m;
    
        QObject::connect(&t,SIGNAL(started()),&m,SLOT(getStarted()));
        QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot()));
    
        m.moveToThread(&t);
        t.moveToThread(&t);
    
        t.start();
        return a.exec();
    }
    View Code

    研究槽函数的具体执行线程有什么意义?
    当信号的发送与对应槽函数的执行在不同线程中,可能产生临界资源的竞争问题

    比如说,在run函数对某一个临界资源进行修改,在槽函数中也对临界资源进行修改,槽函数的调用是在另一个线程中完成的,此时调用槽函数的线程和本身的线程就可能产生竞争问题。

     

  • 相关阅读:
    创建文件并写入内容
    java自动压缩文件并加密
    Java自动发送带图片邮件(带附件)
    eclipse正则表达式搜索
    解决:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):
    spring整合mybatis(原始dao和mapper代理两种方式)
    (解决)mysql1366中文显示错误的终极解决方案
    pdf合并、拆分工具
    ScheduledExecutorService定时方法
    json数据包含特殊字符解析失败
  • 原文地址:https://www.cnblogs.com/-glb/p/13463626.html
Copyright © 2011-2022 走看看