﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-andreitang</title><link>http://www.cppblog.com/andreitang/</link><description /><language>zh-cn</language><lastBuildDate>Mon, 13 Apr 2026 00:30:04 GMT</lastBuildDate><pubDate>Mon, 13 Apr 2026 00:30:04 GMT</pubDate><ttl>60</ttl><item><title>Qt那点事儿（一）</title><link>http://www.cppblog.com/andreitang/archive/2011/08/26/154392.html</link><dc:creator>樱桃小锤子</dc:creator><author>樱桃小锤子</author><pubDate>Fri, 26 Aug 2011 02:41:00 GMT</pubDate><guid>http://www.cppblog.com/andreitang/archive/2011/08/26/154392.html</guid><wfw:comment>http://www.cppblog.com/andreitang/comments/154392.html</wfw:comment><comments>http://www.cppblog.com/andreitang/archive/2011/08/26/154392.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cppblog.com/andreitang/comments/commentRss/154392.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/andreitang/services/trackbacks/154392.html</trackback:ping><description><![CDATA[<h1>第一回 Signal和Slot是同步的还是异步的？</h1>
<p>　　我们知道Qt以他的signal和slot机制独步天下。但大家在用的时候有没有注意过，signal和slot之间是异步的，还是同步的呢？为此我问过不少使用Qt的道友。有人说是同步的，有人说是异步的，也有人说要看当时你的人品。:( #$%^&amp;*</p>
<p>　　为此贫道，特别做了以下几个测试：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; First,在main()主函数里，设置两个基于QObject为父类的对象a和b，a触发signal,b接受signal。请看具体案例：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;</p>
<div class="cnblogs_code">
<pre><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> MyTestA : </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> QObject<br /></span><span style="color: #008080;"> 2</span> <span style="color: #000000;">{<br /></span><span style="color: #008080;"> 3</span> <span style="color: #000000;">    Q_OBJECT<br /></span><span style="color: #008080;"> 4</span> <span style="color: #000000;"></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br /></span><span style="color: #008080;"> 5</span> <span style="color: #000000;">    </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> emitSignal()<br /></span><span style="color: #008080;"> 6</span> <span style="color: #000000;">    {<br /></span><span style="color: #008080;"> 7</span> <span style="color: #000000;">        signalMyTestA();<br /></span><span style="color: #008080;"> 8</span> <span style="color: #000000;">    }<br /></span><span style="color: #008080;"> 9</span> <span style="color: #000000;"><br /></span><span style="color: #008080;">10</span> <span style="color: #000000;"></span><span style="color: #0000ff;">public</span><span style="color: #000000;"> slots:<br /></span><span style="color: #008080;">11</span> <span style="color: #000000;">    </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> slotMyTestA()<br /></span><span style="color: #008080;">12</span> <span style="color: #000000;">    {<br /></span><span style="color: #008080;">13</span> <span style="color: #000000;">        qDebug()</span><span style="color: #000000;">&lt;&lt;</span><span style="color: #800000;">"</span><span style="color: #800000;">slotMyTestA is called.</span><span style="color: #800000;">"</span><span style="color: #000000;">;<br /></span><span style="color: #008080;">14</span> <span style="color: #000000;">    }<br /></span><span style="color: #008080;">15</span> <span style="color: #000000;">signals:<br /></span><span style="color: #008080;">16</span> <span style="color: #000000;">    </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> signalMyTestA();<br /></span><span style="color: #008080;">17</span> <span style="color: #000000;">};<br /></span><span style="color: #008080;">18</span> <span style="color: #000000;"><br /></span><span style="color: #008080;">19</span> <span style="color: #000000;"></span><span style="color: #0000ff;">class</span><span style="color: #000000;"> MyTestB : </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> QObject<br /></span><span style="color: #008080;">20</span> <span style="color: #000000;">{<br /></span><span style="color: #008080;">21</span> <span style="color: #000000;">    Q_OBJECT<br /></span><span style="color: #008080;">22</span> <span style="color: #000000;"></span><span style="color: #0000ff;">public</span><span style="color: #000000;"> slots:<br /></span><span style="color: #008080;">23</span> <span style="color: #000000;">    </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> slotMyTestB()<br /></span><span style="color: #008080;">24</span> <span style="color: #000000;">    {<br /></span><span style="color: #008080;">25</span> <span style="color: #000000;">        qDebug()</span><span style="color: #000000;">&lt;&lt;</span><span style="color: #800000;">"</span><span style="color: #800000;">slotMyTestB is called.</span><span style="color: #800000;">"</span><span style="color: #000000;">;<br /></span><span style="color: #008080;">26</span> <span style="color: #000000;">    }<br /></span><span style="color: #008080;">27</span> <span style="color: #000000;">signals:<br /></span><span style="color: #008080;">28</span> <span style="color: #000000;">    </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> signalMyTestB();<br /></span><span style="color: #008080;">29</span> <span style="color: #000000;">};<br /></span><span style="color: #008080;">30</span> <span style="color: #000000;"><br /></span><span style="color: #008080;">31</span> <span style="color: #000000;"></span><span style="color: #0000ff;">int</span><span style="color: #000000;"> main(</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> argc, </span><span style="color: #0000ff;">char</span><span style="color: #000000;"> </span><span style="color: #000000;">*</span><span style="color: #000000;">argv[])<br /></span><span style="color: #008080;">32</span> <span style="color: #000000;">{<br /></span><span style="color: #008080;">33</span> <span style="color: #000000;">    QApplication app(argc, argv);<br /></span><span style="color: #008080;">34</span> <span style="color: #000000;"><br /></span><span style="color: #008080;">35</span> <span style="color: #000000;">    MyTestA a;<br /></span><span style="color: #008080;">36</span> <span style="color: #000000;">    MyTestB b;<br /></span><span style="color: #008080;">37</span> <span style="color: #000000;">    QObject::connect(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">a,SIGNAL(signalMyTestA()),</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">b,SLOT(slotMyTestB()));<br /></span><span style="color: #008080;">38</span> <span style="color: #000000;"><br /></span><span style="color: #008080;">39</span> <span style="color: #000000;">    a.emitSignal();<br /></span><span style="color: #008080;">40</span> <span style="color: #000000;">    <br /></span><span style="color: #008080;">41</span> <span style="color: #000000;">    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> app.exec();<br /></span><span style="color: #008080;">42</span> <span style="color: #000000;">}</span></pre>
</div>
<p>在slotMyTestB的函数里打个断点，看一下调用堆栈(call stack)。</p>
<p>是同步调用的，某些道友开始拈胡微笑，实践出真知啊。</p>
<p><img src="http://pic002.cnblogs.com/images/2011/320623/2011080310273623.png" alt="" /></p>
<p>此时只见东方黑云滚滚，电闪雷鸣，又有道友开始度劫了。突然一度劫道友横眉冷对，拿起拂尘刷刷的改写了上面的代码。只见此道友把a对象挪到了一个新线程中（MyTestC创建的），而b对象仍然在主线程中。然后a对象触发信号。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:cpp;gutter:true;">class MyTestA : public QObject
{
    Q_OBJECT
public:
    void emitSignal()
    {
        signalMyTestA();
    }
public slots:
    void slotMyTestA()
    {
        qDebug()&lt;&lt;"slotMyTestA is called.";
    }
signals:
    void signalMyTestA();
};
class MyTestB : public QObject
{
    Q_OBJECT
public slots:
    void slotMyTestB()
    {
        qDebug()&lt;&lt;"slotMyTestB is called.";
    }
signals:
    void signalMyTestB();
};
extern MyTestB *g_pMyTestB;
class MyTestC : public QThread
{
    Q_OBJECT
public:
    void run()
    {
        MyTestA a;
        connect(&amp;a,SIGNAL(signalMyTestA()),g_pMyTestB,SLOT(slotMyTestB()));
        a.emitSignal();
        exec();
    }
public slots:
    void slotMyTestC()
    {
        qDebug()&lt;&lt;"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 = &amp;b;
    MyTestC c;
    c.start();
    return app.exec();
}
</pre>
</div>
<p>说时迟，那时快。在一道紫雷劈下之际，按下了F5。只见，此时的调用堆栈显示,</p>
<p><img src="http://pic002.cnblogs.com/images/2011/320623/2011080311064522.png" alt="" /></p>
<p>奇迹出现了，居然变成异步调用了。只见东方天空万道金光射下，在阵阵仙乐声中，传来朗朗之声:"贫道尘事已了，再无牵挂"。</p>
<p>难道Qt真的是靠人品的，或者Qt莫不是也是修仙道友，不日也将飞升。</p>
<p>在吾等众人膜拜加疑惑之时，只见飞升前辈，留下一条偈语。内事不决问百度，外事不决问谷歌。</p>
<p>吾等众人立刻搜寻，恍然大物。</p>
<p><strong><span style="color: #ff0000;">原来signal和slot是异步调用还是同步调用，取决于对connect的设定。其实connect还有一个参数(Qt::ConnectionType),是它决定了是同步还是异步。</span></strong>以下是ConnectionType的定义</p>
<p><img src="http://pic002.cnblogs.com/images/2011/320623/2011080311260585.png" alt="" /></p>
<p>只不过，平常它有一个默认值Qt::AutoConnection,我们忽略了它。这时有道友问道，为何在AutoConnection模式下，有时是同步，有时是异步，莫非Auto就是人品代名词。</p>
<p>非也，其实Auto是这样规定的，</p>
<p><strong><span style="color: #ff0000;">当sender和receiver在同一线程时，就是同步模式，而在不同线程时，则是异步模式。</span></strong></p>
<p><span style="color: #000000;">众人皆曰善。</span></p>
<p><span style="color: #000000;">就在众人弹冠相庆之时，突然一道类似眼镜发出的寒光闪过，一个黑影渐渐清晰了起来。</span></p>
<p><span style="color: #000000;">他居然就是..................</span></p>
<p><span style="color: #000000;">青春永驻，十二年如一日的柯南君，他招牌式的磁性声音给众道友一晴天霹雳，&#8220;诸位以为这就是全部的真相吗？&#8221;</span></p>
<p><span style="color: #000000;">接着他刷刷的又改写了代码，在主线程中生成a,b两个对象，而a对象在新线程（MyTestC创建的）中触发信号。</span></p>
<p>&nbsp;</p>
<div class="cnblogs_Highlighter">
<pre class="brush:cpp;gutter:true;">class MyTestA : public QObject
{
    Q_OBJECT
public:
    void emitSignal()
    {
        signalMyTestA();
    }
public slots:
    void slotMyTestA()
    {
        qDebug()&lt;&lt;"slotMyTestA is called.";
    }
signals:
    void signalMyTestA();
};
class MyTestB : public QObject
{
    Q_OBJECT
public slots:
    void slotMyTestB()
    {
        qDebug()&lt;&lt;"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-&gt;emitSignal();
        exec();
    }
public slots:
    void slotMyTestC()
    {
        qDebug()&lt;&lt;"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 = &amp;a;
    MyTestB b;
    g_pMyTestB = &amp;b;
    QObject::connect(&amp;a,SIGNAL(signalMyTestA()),&amp;b,SLOT(slotMyTestB()));
    MyTestC c;
    c.start();
    return app.exec();
}
</pre>
</div>
<p>&nbsp;</p>
<p>在众人疑惑的眼光中，此君淡定的按下了F5。只见调用堆栈(call stack)显示</p>
<p><img src="http://pic002.cnblogs.com/images/2011/320623/2011080312020647.png" alt="" /></p>
<p>众人皆惊呼，&#8220;Impossible&#8221;。a和b明明是属于一个线程的，为何会异步调用。此时我们熟悉的语录，又在耳边回响,是"我相信真相只有一个！！！"这句话吗？No，只见柯南君，优雅地挥了挥手指，"Nothing impossible",从口中缓缓滑出。</p>
<p>。众人皆扑街，&#8220;有屁快放&#8221;。</p>
<p>此时柯南君缓缓从口袋中，摸出一张纸，抛向空中，然后转身离去。只见随风飘落的纸面上面摘录了这么一段Qt源代码，在Auto模式下，如果要同步调用，不仅要求sender和receiver是同一线程，而且sender触发的时候，所在的线程也要和receiver一致。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:cpp;gutter:true;"> // determine if this connection should be sent immediately or
            // put into the event queue
            if ((c-&gt;connectionType == Qt::AutoConnection
                 &amp;&amp; (currentThreadData != sender-&gt;d_func()-&gt;threadData
                     || receiver-&gt;d_func()-&gt;threadData != sender-&gt;d_func()-&gt;threadData))
                || (c-&gt;connectionType == Qt::QueuedConnection)) {
                queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);
                continue;
            } else if (c-&gt;connectionType == Qt::BlockingQueuedConnection) {
                blocking_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);
                continue;
            }<br /><br />摘自qobject.cpp  </pre>
</div>
<p>z众人皆惊，<strong><span style="color: #ff0000;">原来在Auto模式下，如果sender的触发时所处的线程和receiver不同，也会是异步调用</span></strong>。此时道友齐声向柯南喊道&#8220;这是全部的真相了吗&#8221;？柯南转过头来笑而不语，渐渐又消失在黑暗中。&#8220;有多少无耻可以重来&#8221;漂上了众人的心头。望着远处的雨后阳光，一个大大的问号也出现在众人头顶,"Qt你到底有多无耻？？？"。众人又陷入了沉思。</p>
<p>欲知后事如何，请听下回分解。</p>
<p>&nbsp;</p>
<p>此篇已在<a href="http://www.cnblogs.com/andreitang/archive/2011/08/03/2125815.html">CNBLOG</a>同时发布</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="color: #000000;"><br /></span></p>
<p><span style="color: #000000;"><br /></span></p>
<p><span style="color: #000000;"><br /></span></p><img src ="http://www.cppblog.com/andreitang/aggbug/154392.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/andreitang/" target="_blank">樱桃小锤子</a> 2011-08-26 10:41 <a href="http://www.cppblog.com/andreitang/archive/2011/08/26/154392.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>