• Qt那点事儿(一)


    原文http://www.cnblogs.com/andreitang/archive/2011/08/03/2125815.html

    第一回 Signal和Slot是同步的还是异步的?

      我们知道Qt以他的signal和slot机制独步天下。但大家在用的时候有没有注意过,signal和slot之间是异步的,还是同步的呢?为此我问过不少使用Qt的道友。有人说是同步的,有人说是异步的,也有人说要看当时你的人品。:( #$%^&*

      为此贫道,特别做了以下几个测试:

         First,在main()主函数里,设置两个基于QObject为父类的对象a和b,a触发signal,b接受signal。请看具体案例:

        

    复制代码
     1 class MyTestA : public QObject
    2 {
    3 Q_OBJECT
    4 public:
    5 void emitSignal()
    6 {
    7 signalMyTestA();
    8 }
    9
    10 public slots:
    11 void slotMyTestA()
    12 {
    13 qDebug()<<"slotMyTestA is called.";
    14 }
    15 signals:
    16 void signalMyTestA();
    17 };
    18
    19 class MyTestB : public QObject
    20 {
    21 Q_OBJECT
    22 public slots:
    23 void slotMyTestB()
    24 {
    25 qDebug()<<"slotMyTestB is called.";
    26 }
    27 signals:
    28 void signalMyTestB();
    29 };
    30
    31 int main(int argc, char *argv[])
    32 {
    33 QApplication app(argc, argv);
    34
    35 MyTestA a;
    36 MyTestB b;
    37 QObject::connect(&a,SIGNAL(signalMyTestA()),&b,SLOT(slotMyTestB()));
    38
    39 a.emitSignal();
    40
    41 return app.exec();
    42 }
    复制代码

    在slotMyTestB的函数里打个断点,看一下调用堆栈(call stack)。

    是同步调用的,某些道友开始拈胡微笑,实践出真知啊。

    此时只见东方黑云滚滚,电闪雷鸣,又有道友开始度劫了。突然一度劫道友横眉冷对,拿起拂尘刷刷的改写了上面的代码。只见此道友把a对象挪到了一个新线程中(MyTestC创建的),而b对象仍然在主线程中。然后a对象触发信号。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    class MyTestA : public QObject
    {
        Q_OBJECT
    public:
        void emitSignal()
        {
            signalMyTestA();
        }
     
    public slots:
        void slotMyTestA()
        {
            qDebug()<<"slotMyTestA is called.";
        }
    signals:
        void signalMyTestA();
    };
     
    class MyTestB : public QObject
    {
        Q_OBJECT
    public slots:
        void slotMyTestB()
        {
            qDebug()<<"slotMyTestB is called.";
        }
    signals:
        void signalMyTestB();
    };
     
    extern MyTestB *g_pMyTestB;
    class MyTestC : public QThread
    {
        Q_OBJECT
    public:
     
        void run()
        {
            MyTestA a;
            connect(&a,SIGNAL(signalMyTestA()),g_pMyTestB,SLOT(slotMyTestB()));
            a.emitSignal();
            
            exec();
            
     
        }
     
    public slots:
        void slotMyTestC()
        {
            qDebug()<<"slotMyTestC is called.";
        }
    signals:
        void signalMyTestC();
     
     
    };
     
    class MyTest : public QDialog
    {
        Q_OBJECT
     
    public:
        MyTest(QWidget *parent = 0, Qt::WFlags flags = 0);
        ~MyTest();
     
    private:
        Ui::MyTestClass ui;
    };
     
    ////////////////////////////////////////////////
    MyTestB *g_pMyTestB = NULL;
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
     
         
        MyTestB b;
        g_pMyTestB = &b;
        MyTestC c;
     
        c.start();
         
        return app.exec();
    }

    说时迟,那时快。在一道紫雷劈下之际,按下了F5。只见,此时的调用堆栈显示,

    奇迹出现了,居然变成异步调用了。只见东方天空万道金光射下,在阵阵仙乐声中,传来朗朗之声:"贫道尘事已了,再无牵挂"。

    难道Qt真的是靠人品的,或者Qt莫不是也是修仙道友,不日也将飞升。

    在吾等众人膜拜加疑惑之时,只见飞升前辈,留下一条偈语。内事不决问百度,外事不决问谷歌。

    吾等众人立刻搜寻,恍然大物。

    原来signal和slot是异步调用还是同步调用,取决于对connect的设定。其实connect还有一个参数(Qt::ConnectionType),是它决定了是同步还是异步。以下是ConnectionType的定义

    只不过,平常它有一个默认值Qt::AutoConnection,我们忽略了它。这时有道友问道,为何在AutoConnection模式下,有时是同步,有时是异步,莫非Auto就是人品代名词。

    非也,其实Auto是这样规定的,

    当sender和receiver在同一线程时,就是同步模式,而在不同线程时,则是异步模式。

    众人皆曰善。

    就在众人弹冠相庆之时,突然一道类似眼镜发出的寒光闪过,一个黑影渐渐清晰了起来。

    他居然就是..................

    青春永驻,十二年如一日的柯南君,他招牌式的磁性声音给众道友一晴天霹雳,“诸位以为这就是全部的真相吗?”

    接着他刷刷的又改写了代码,在主线程中生成a,b两个对象,而a对象在新线程(MyTestC创建的)中触发信号。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    class MyTestA : public QObject
    {
        Q_OBJECT
    public:
        void emitSignal()
        {
            signalMyTestA();
        }
     
    public slots:
        void slotMyTestA()
        {
            qDebug()<<"slotMyTestA is called.";
        }
    signals:
        void signalMyTestA();
    };
     
    class MyTestB : public QObject
    {
        Q_OBJECT
    public slots:
        void slotMyTestB()
        {
            qDebug()<<"slotMyTestB is called.";
        }
    signals:
        void signalMyTestB();
    };
     
    extern MyTestB *g_pMyTestB;
    extern MyTestA *g_pMyTestA;
    class MyTestC : public QThread
    {
        Q_OBJECT
    public:
     
        void run()
        {
            g_pMyTestA->emitSignal();
            exec();
            
     
        }
     
    public slots:
        void slotMyTestC()
        {
            qDebug()<<"slotMyTestC is called.";
        }
    signals:
        void signalMyTestC();
     
     
    };
    /////////////////////////////////////////////
    MyTestB *g_pMyTestB = NULL;
    MyTestA *g_pMyTestA = NULL;
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
     
        MyTestA a;
        g_pMyTestA = &a;
        MyTestB b;
        g_pMyTestB = &b;
     
        QObject::connect(&a,SIGNAL(signalMyTestA()),&b,SLOT(slotMyTestB()));
     
        MyTestC c;
     
        c.start();
         
        return app.exec();
    }

    在众人疑惑的眼光中,此君淡定的按下了F5。只见调用堆栈(call stack)显示

    众人皆惊呼,“Impossible”。a和b明明是属于一个线程的,为何会异步调用。此时我们熟悉的语录,又在耳边回响,是"我相信真相只有一个!!!"这句话吗?No,只见柯南君,优雅地挥了挥手指,"Nothing impossible",从口中缓缓滑出。

    。众人皆扑街,“有屁快放”。

    此时柯南君缓缓从口袋中,摸出一张纸,抛向空中,然后转身离去。只见随风飘落的纸面上面摘录了这么一段Qt源代码,在Auto模式下,如果要同步调用,不仅要求sender和receiver是同一线程,而且sender触发的时候,所在的线程也要和receiver一致。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // determine if this connection should be sent immediately or
               // put into the event queue
               if ((c->connectionType == Qt::AutoConnection
                    && (currentThreadData != sender->d_func()->threadData
                        || receiver->d_func()->threadData != sender->d_func()->threadData))
                   || (c->connectionType == Qt::QueuedConnection)) {
                   queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);
                   continue;
               else if (c->connectionType == Qt::BlockingQueuedConnection) {
                   blocking_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);
                   continue;
               }<br><br>摘自qobject.cpp

    z众人皆惊,原来在Auto模式下,如果sender的触发时所处的线程和receiver不同,也会是异步调用。此时道友齐声向柯南喊道“这是全部的真相了吗”?柯南转过头来笑而不语,渐渐又消失在黑暗中。“有多少无耻可以重来”漂上了众人的心头。望着远处的雨后阳光,一个大大的问号也出现在众人头顶,"Qt你到底有多无耻???"。众人又陷入了沉思。

    欲知后事如何,请听下回分解。

  • 相关阅读:
    RTX这种东西究竟有什么价值?
    为什么必须要有需求文档这种东西?
    如何理解环境光?
    文字校对应该怎么校对?
    绑定解绑钝化活化监听器
    ServletRequestLister
    HttpSessionListener
    ServletContextListener知识点
    案例8-站内搜索功能
    案例7-用户名异步校验
  • 原文地址:https://www.cnblogs.com/yzl050819/p/6813440.html
Copyright © 2020-2023  润新知