写下这个给本身备忘,关于事件循环以及多线程方面的东西我还须要多多学习。首先咱们都知道程序有一个主线程,在GUI程序中这个主线程也叫GUI线程,图形和绘图相关的函数都是由主线程来提供。主线程有个事件循环Event Loop,其实就是一个死循环在不断的等待你的消息队列,经过消息队列完成响应用户操做,绘图,以及相关操做。咱们都知道QDialog有一个exec函数,这个函数会造成“模态”对话框,而后等待用户去输入OK仍是Cancel,不然他毫不返回,以下多线程
void test() { QDialog dialog; dialog.exec(); qDebug() << "finish exec"; }
咱们能够看这个简单的例子,能够看到,当dialog被exec以后,咱们的qDebug是不会输出的,除非咱们人为去点了对话框的OK,不然,就会一直卡在exec之上。这个时候可能同窗会有我一开始同样的误解,咱们会误觉得此时事件循环中止了,其实并非中止,而是阻塞住了。为何会阻塞?由于test这个函数没有返回嘛!异步
m_stateManager->postEvent(ev);
投递事件官方API也说的很清楚,会当即返回,因此别去担忧此时投递的事件进入不到消息队列,真正要关心的是此时dialog的exec让你的主线程阻塞了,这个时候消息队列上的事件都不会进行操做,都在等待dialog的返回,只有dialog返回,接下来的事件才会依次进行。要记住,消息是能够正常投递的。函数
那么,有没有办法可让dialog.exec()当即返回,同时个人对话框还在呢?方法是有的oop
void test() { metaObject()->invokeMethod(this, "invokeTest", Qt::QueuedConnection); qDebug() << "finish exec"; } Q_INVOKABLE void invokeTest() { QDialog dialog; dialog.exec(); }
答案就是用Qt提供的元对象系统Meta Object System的invokeMethod,而且将第三个参数设为Qt:QueuedConnection。从字面上咱们也能够看的出来,这个调用不会去当即调用,相反他是异步的,他会把这个函数投递到事件队列里,也就是说,这个例子中的qDebug会输出,输出以后,事件队列才会去调用dialog.exec()这个函数,固然了,调用这个函数以后又会达到咱们一开始的那个阻塞的效果,你经过异步到最后的触发,始终你须要去面对exec给你当前事件循环形成阻塞的问题。post
让咱们再深刻一点,当一个事件循环的时候还算简单。可是咱们知道,Qt中对于状态机他是有一个异步的事件循环的,也就是说外面有事件循环,状态机自己也有事件循环。好比学习
m_stateManager->postEvent(ev);
m_tool->run();
你给状态机投递了一个事件,他根据状态迁移去调用你的tool,这一切看起来很美好,但若是你此时的tool是个跟以前同样的阻塞的exec呢?让咱们来看一下。this
void Tool::run() { QDialog dialog; dialog.exec(); }
对于这个状况,当咱们运行tool以后,咱们的状态机就跟以前的主事件事件循环同样被阻塞了,也就是说若是我此时继续spa
m_stateManager->postEvent(ev2);
和以前同样,这个postEvent会当即返回,由于投递到事件队列都是当即返回的。可是关键的问题在于你的状态机整个事件循环都中止不动了,都在等你以前的tool运行结束,但由于你以前的tool是个dialog.exec()你必须手动去点OK,否则你的状态机事件循环就阻塞不动了,这个时候若是你的客户不断的去点你这个tool的event,那会产生噩梦般的效果----你点完OK以后又会来OK以后又会来OK。。。这其实就是你一旦点了OK,你的消息队列就又能够循环了,以前等待的ev就都会去执行了。并且要注意的就是,此时你的exec的执行在主线程上,只是不能进行返回,但仍是能够接收诸如键盘,鼠标等事件投递。线程
前面也说了,事件循环和状态机循环是两个独立的循环,其实这也很好理解。若是没有事件循环,状态机事件怎么知道你有没有按下这个键?从而去投递给状态机呢?其实也就是说当你状态机事件阻塞的时候,你的主事件循环还在不断的接收你的键盘和鼠标的操做,这一点是没有影响的。code
所以,要想实如今tool的时候我还能相应别的状态事件,其实作法也是同样的,就是
void Tool::run() { metaObject()->invokeMethod(this, "invokeTest", Qt::QueuedConnection); } Q_INVOKABLE void invokeTest() { QDialog dialog; dialog.exec(); }
当即返回,这个”当即返回“并非说你的事情作完了,而是你更想让状态机可以进行以后事件的循环,别去由于你的dialog而耽误了你们。
最后说说模态这个主题,其实模态的理解就是你的消息队列都在正常进行,由于你不断的在等待,致使事件循环不能进行下去,必须你这边正常返回,你接下来的操做才能继续。
今天又从新思考了一下这个问题。同步的意思彷佛就是必需要执行完成才能返回。异步的概念就是当即返回,以后执行,会把他扔到消息队列里,待同步函数处理完以后,而后去搜索事件队列进行操做。其实状态机归根结底就是一堆信号连接,只是他的方向是规矩状态迁移表来进行。做为主线程的Event Loop来讲,当dialog进入exec的时候,就是就是在进行事件队列,并非说他此时把事件队列给阻塞了,这个我以前理解有问题。exec的含义就是去处理事件队列,去处理事件循环,去检索当前还有哪些事件能够被处理,从而去正常处理。好比咱们有一个主程序窗口MainWindow,有一个Dialog,此时咱们去调用dialog的exec,内部会去建立一个QEventLoop,又由于这个dialog的所在线程和MainWindow在同一个线程上,因此看上去彷佛是两个EventLoop,但实际上都是同一个线程的Event Loop(一个线程只能有一个Event Loop,这是原子性问题)。因此在dialog进行exec的时候他会去检索主线程上的事件队列去操做。
而咱们以前讲的状态机,其实仔细想了想很简单,你就把他理解成是主程序总的Event Loop中的一个事件,他在进行操做的时候,不返回(tool去调用dialog的exec看上去彷佛进行了事件循环在等待你新的event,但别忘了,你自己这个tool的run就是经过事件队列去触发的)因此必需要等待这个tool的exec返回,你的事件队列才能正常下去。
再次强调: