网游里的那点事儿,Qt那点事儿(一)

第一回 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)。
是同步调用的,某些道友开始拈胡微笑,实践出真知啊。
Qt那点事儿(一)网游里的那点事儿
此时只见东方黑云滚滚,电闪雷鸣,又有道友开始度劫了。突然一度劫道友横眉冷对,拿起拂尘刷刷的改写了上面的代码。只见此道友把a对象挪到了一个新线程中(MyTestC创建的),而b对象仍然在主线程中。然后a对象触发信号。
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真的是靠人品的,或者Qt莫不是也是修仙道友,不日也将飞升。
在吾等众人膜拜加疑惑之时,只见飞升前辈,留下一条偈语。内事不决问百度,外事不决问谷歌。
吾等众人立刻搜寻,恍然大物。
原来signal和slot是异步调用还是同步调用,取决于对connect的设定。其实connect还有一个参数(Qt::ConnectionType),是它决定了是同步还是异步。以下是ConnectionType的定义
Qt那点事儿(一)网游里的那点事儿
只不过,平常它有一个默认值Qt::AutoConnection,我们忽略了它。这时有道友问道,为何在AutoConnection模式下,有时是同步,有时是异步,莫非Auto就是人品代名词。
非也,其实Auto是这样规定的,
当sender和receiver在同一线程时,就是同步模式,而在不同线程时,则是异步模式。
众人皆曰善。
就在众人弹冠相庆之时,突然一道类似眼镜发出的寒光闪过,一个黑影渐渐清晰了起来。
他居然就是..................
青春永驻,十二年如一日的柯南君,他招牌式的磁性声音给众道友一晴天霹雳,“诸位以为这就是全部的真相吗?”
接着他刷刷的又改写了代码,在主线程中生成a,b两个对象,而a对象在新线程(MyTestC创建的)中触发信号。
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)显示
Qt那点事儿(一)网游里的那点事儿
众人皆惊呼,“Impossible”。a和b明明是属于一个线程的,为何会异步调用。此时我们熟悉的语录,又在耳边回响,是"我相信真相只有一个!!!"这句话吗?No,只见柯南君,优雅地挥了挥手指,"Nothing impossible",从口中缓缓滑出。
。众人皆扑街,“有屁快放”。
此时柯南君缓缓从口袋中,摸出一张纸,抛向空中,然后转身离去。只见随风飘落的纸面上面摘录了这么一段Qt源代码,在Auto模式下,如果要同步调用,不仅要求sender和receiver是同一线程,而且sender触发的时候,所在的线程也要和receiver一致。
// 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; } 摘自qobject.cpp
z众人皆惊,原来在Auto模式下,如果sender的触发时所处的线程和receiver不同,也会是异步调用。此时道友齐声向柯南喊道“这是全部的真相了吗”?柯南转过头来笑而不语,渐渐又消失在黑暗中。“有多少无耻可以重来”漂上了众人的心头。望着远处的雨后阳光,一个大大的问号也出现在众人头顶,"Qt你到底有多无耻???"。众人又陷入了沉思。
欲知后事如何,请听下回分解。
此篇已在CSDN同时发布




Tags: 

延伸阅读

最新评论

发表评论