• 11846阅读
  • 26回复

Qt多个槽被触发,怎么处理?? [复制链接]

上一主题 下一主题
离线lbknxy
 

只看楼主 正序阅读 楼主  发表于: 2014-06-23
— 本帖被 alexltr 从 Qt中文处理 移动到本区(2014-06-23) —
如题,当一个槽正在执行并且还没执行结束,这时其他事件又触发另一个槽会出现什么结果,后来的这个信号所触发的这个槽的执行会阻塞吗(单线程
离线lf460

只看该作者 26楼 发表于: 2015-03-18
真的是获益匪浅
同样的信号槽机制,针对不同的“场景”,不同的系统,会产生不同的效果。
离线labhome

只看该作者 25楼 发表于: 2014-06-25
又复习了一遍,又更正了一次自己的知识,受教,谢谢!
离线lbknxy

只看该作者 24楼 发表于: 2014-06-25
受益匪浅
离线alexltr

只看该作者 23楼 发表于: 2014-06-24
QTCN就需要这样的讨论! 收益匪浅啊。
我不从事IT,只是喜欢Qt。
我不是程序员,只是与程序有缘。
我写程序,只是为了让工作变得简单有序!

                      ----  一个一直在入门的编程学习者
离线dbzhang800

只看该作者 22楼 发表于: 2014-06-24
回 begboy 的帖子
begboy:当一个信号被发射时,与其相关联的槽将被立刻执行,
Qt真的确保这一点吗?
我们以分时系统UNIX(LINUX)为例来深入思考一下:
为满足多用户多任务的要求,现代的操作系统一般会将用户
....... (2014-06-24 19:04) 

恩,这对事件系统的描述是很贴切的。事件到信号转换的过程,会有这种问题;queue方式的信号槽,也会有这种问题。

不过 labhome 那句话是针对 direct 连接的信号槽来说的,和Qt的事件系统无关,所以不存在这种问题。
离线begboy

只看该作者 21楼 发表于: 2014-06-24
当一个信号被发射时,与其相关联的槽将被立刻执行,

Qt真的确保这一点吗?
我们以分时系统UNIX(LINUX)为例来深入思考一下:
为满足多用户多任务的要求,现代的操作系统一般会将用户
进程(程序)执行分成多个级别,简化来讲主要分成用户和核心2级。
核心一级主要负责对CPU的时间调度、周边硬件设备的管理、中断的处理、
进程调度、存储管理等等。
当QT在UNIX执行信号槽时,核心如何处理呢?
第一种情况:信号槽不涉及硬件资源的处理;
第二种情况:信号槽涉及硬件资源的处理;
对于第一种情况,系统仅以软件中断级别处理(低优先级)
对于第二种情况,系统则进入硬件中断级别处理(高优先级)
以经典的UNIX类系统中断级来参考:
从高到低:
1、    机器错误;
2、    时钟;
3、    磁盘;
4、    网络设备;
5、    终端;
6、    软件中断;
我们大部分程序的运行涉及中断的基本在软件中断这个低优先级层次,
是极容易被高优先级中断的,对于被中断的用户进程核心会做一个上下文
切换。但核心一定会长期保持这个上下文映象吗?非也。当核心一直有
高优先级的中断需要处理,当用户进程的挂起超过操作系统核心设定的冻结时间,
用户进程在不同操作系统结果是不同的,甚至进程被取消。
举个例子可能会更明了:
有个屁民张三感冒了,一开始只是打个喷嚏。他到了
省城最大的医院去看病。挂号排队,排第八。等啊等啊,
前面七个看完了,终于轮到他,这时张三已经开始流鼻水了。
忽然来了一个院长太太,护士长直接带进医生处理了,张三
只好继续等;终于院长太太走了,张三刚一站起来,又来了
个县长夫人,张三又坐下了。等啊等啊,又来了个市长夫人,
张三又坐下了。这时张三已经发抖了。。。等啊等啊,又来了个省长夫人,
。。。最终来了个宇宙夫人。。。。张三再也站不起来了。。。。

故事纯属虚构,大家就乐乐吧。
begboy



离线dbzhang800

只看该作者 20楼 发表于: 2014-06-24
回 labhome 的帖子
labhome:
常规情况下,你可以认为信号槽机制就是一个优化了过的,不会产生 core dumps 的回调函数。如果深点说,Qt的信号槽机制其实就是按照名称查表,类似C++虚函数表机制,Qt是查元数据表。
首先生成表,发射信号时:
void MainWindow::emit1()
{
// 首先把参数打包,此处没有
.......


恩,说的不错,挺清楚的。

不过有一点需要注意一下,你提到的槽顺序的说法已经过时了。

如果存在多个槽与某个信号相关联,那么,当这个信号被发射时,这些槽将会一个接一个地 执行,但是它们执行的顺序将会是随机的、不确定的,我们不能人为地指定哪个先执行、哪 个后执行。
离线labhome

只看该作者 19楼 发表于: 2014-06-24
常规情况下,你可以认为信号槽机制就是一个优化了过的,不会产生 core dumps 的回调函数。如果深点说,Qt的信号槽机制其实就是按照名称查表,类似C++虚函数表机制,Qt是查元数据表。
首先生成表,发射信号时:
void MainWindow::emit1()
{
// 首先把参数打包,此处没有
    
// 然后调用元数据类的激活
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

查表,然后调用:qt_metacall(),

// 响应信号是在moc里实现的
int MainWindow::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
    ....
        // 这里调用真正的方法
        switch (_id) {
        case 0:***
        }
    ....
}

要是想深入,就静下心看源码,一、两个小时的事,要不就简单理解就行了。
离线labhome

只看该作者 18楼 发表于: 2014-06-24
main.cpp:
return app.exec();


qapplication.cpp:
int QApplication::exec()
{
    return QGuiApplication::exec();
}



qguiapplication.cpp:
int QGuiApplication::exec()
{
#ifndef QT_NO_ACCESSIBILITY
    QAccessible::setRootObject(qApp);
#endif
    return QCoreApplication::exec();
}


qcoreapplication.cpp:
int QCoreApplication::exec()
{
    ....
    int returnCode = eventLoop.exec();
    ....
}



qeventloop.cpp:
int QEventLoop::exec(ProcessEventsFlags flags)
{
    ....
    while (!d->exit.loadAcquire())
        processEvents(flags | WaitForMoreEvents | EventLoopExec);
    ....
}

大至如此,我没有仔细的看,但看来和windows类似,现代操作系统基本上都是基于事件/消息模式,QT是事件驱动的,在Windows中是Message,在Qt中是Event,Windows中有消息循环和消息派发, 在Qt中有事件循环和事件派发,so,单线程就是顺序执行,即使有中断事件的产生,也是通过发送消息/事件到队列,取消息/事件,执行消息/事件,循环往复,直至取到退出事件。

再想想,为什么定时器的时间不够准确,你定时1秒,再运行一个超过1秒的函数,定时器事件必定要在函数执行完成后才能响应定时器事件。即便系统有硬中断产生,也是同样处理,系统底层是有中断响应的,但到应用层还是通过消息/事件通知的。比如串口,如果不用多线程方式读取,在你执行一个长任务时,串口过来的数据你是不会马上读到的,如果缓冲区不够,而串口数据又太多的话,就会造成串口数据丢失,目前Qt所带的串口类好像有两种方式,一种轮询,一种是事件。

我只是想表达的是,单线程就是顺序执行,没有例外,关键是要理解这个顺序,GUI程序如同单片机程序一样,总有一个 “循环处理”:
main()
{
    // 前处理
    while(主线程.取出事件() != 退出)
        处理事件();
    // 退出后处理
}
我喜欢这样说,GUI程序中,顺序是指main()或其它入口函数中的语句执行顺序,具体到某个事件的话,要看事件在事件队列中的顺序,大致如此。


对于楼主的情况,cahwxy说的槽函数的同步控制什么的,我真心没注意去看,暂时也用不到,就偷懒了,但如果只是使用默认的参数来设置信号槽的话,那就一定是阻塞的。

当一个信号被发射时,与其相关联的槽将被立刻执行,就象一个正常的函数调用一样。信号 - 槽机制完全独立于任何 GUI 事件循环。只有当所有的槽返回以后发射函数(emit)才返回。 如果存在多个槽与某个信号相关联,那么,当这个信号被发射时,这些槽将会一个接一个地 执行,但是它们执行的顺序将会是随机的、不确定的,我们不能人为地指定哪个先执行、哪 个后执行。

connect(this, &MainWindow::emit1, this, &MainWindow::onSlot);

void MainWindow::on_pushButton_clicked()
{
    emit this->emit1();
}

void MainWindow::onSlot()
{
    j++;
    qDebug() << "j" << j;
    emit this->emit1();
    i++;
   qDebug() << i;
}


j 4262
j 4263
j 4264
j 4265
j 4266
j 4267
j 4268
j 4269
j 4270
j 4271
j 4272
j 4273
j 4274
j 4275
j 4276
j 4277
j 4278
j 4279
j 4280
j 4281
j 4282
j 4283
j 4284
j 4285
j 4286
j 4287
j 4288
j 4289
j 4290
j 4291
j 4292
j 4293
j 4294
j 4295
j 4296
j 4297
j 4298
j 4299
j 4300
j 4301
j 4302
j 4303
j 4304
j 4305
j 4306
j 4307
j 4308
j 4309
j 4310
程序异常结束。

嗯,程序永远不会i++,也许例子极端,但就是这样。

这几个字打了一小时,有监控,怕被老板发现

1条评分好评度+1
lbknxy 好评度 +1 - 2014-06-25
离线begboy

只看该作者 17楼 发表于: 2014-06-24
回 dbzhang800 的帖子
dbzhang800:恩,不了解实时操作系统,不过硬件差异确实都是存在的。不清楚Qt的对应版本会不会存在对其额外的特性支持。
考虑到楼主的问题属于Qt的底层运行的基础(单线程信号槽和事件系统),我想在各个平台下应该还是有保证的。 (2014-06-24 11:30) 

呵呵。
deBao兄讲话还是那么客气。
现还在朝阳区吗?
begboy
离线dbzhang800

只看该作者 16楼 发表于: 2014-06-24
回 begboy 的帖子
begboy:提提我个人的看法:
Qt信号槽在不同的操作系统在执行上是存在不同的结果的,毕竟QT只是C++层面上的包装,
也就是说Qt是建立在软件层面上的东西,它没有过多考虑硬中断对其的影响,或者说它把
原子操作这类的问题交给操作系统处理。但不同的操作系统对硬中断和软中断处理方法是
不 .. (2014-06-24 11:10) 

恩,不了解实时操作系统,不过硬件差异确实都是存在的。不清楚Qt的对应版本会不会存在对其额外的特性支持。

考虑到楼主的问题属于Qt的底层运行的基础(单线程信号槽和事件系统),我想在各个平台下应该还是有保证的。
离线dbzhang800

只看该作者 15楼 发表于: 2014-06-24
回 lbknxy 的帖子
lbknxy:恩恩,谢谢女版主
我想的情况是这样的:A信号触发了B槽,在B槽正在执行的过程中,外部事件(比如网络数据的到来)触发了信号C,这时C触发了D槽(D和B是两个不想关的槽)。
我想问的是在单线程中,D是否会阻塞,直到B执行完。 (2014-06-24 11:01) 

对,在前一个槽B执行完之前,信号C根本就不会被触发。

只有B执行完之后,事件循环才能继续运作,事件系统发现队列中有新的外部事件,对该事件进行派发,比如派发给了一个QTcpSocket,该socket对该事件作出回应,emit出信号C
离线begboy

只看该作者 14楼 发表于: 2014-06-24
提提我个人的看法:
Qt信号槽在不同的操作系统在执行上是存在不同的结果的,毕竟QT只是C++层面上的包装,
也就是说Qt是建立在软件层面上的东西,它没有过多考虑硬中断对其的影响,或者说它把
原子操作这类的问题交给操作系统处理。但不同的操作系统对硬中断和软中断处理方法是
不一致的。举个例子:QNX类的实时系统与普通的UNIX系统(如AIX、BSD、HP-UX、LINUX等)甚至Win系统是相差很远,这是和CPU设计有莫大关系。
所以DeBao兄和cahwxy兄讲法其实都没有问题。
真理也往往在讨论中发现,支持大家多讨论但不要涉及个人词语。
您们觉得呢?
begboy
离线lbknxy

只看该作者 13楼 发表于: 2014-06-24
回 lbknxy 的帖子
lbknxy:恩恩,谢谢女版主
我想的情况是这样的:A信号触发了B槽,在B槽正在执行的过程中,外部事件(比如网络数据的到来)触发了信号C,这时C触发了D槽(D和B是两个不想关的槽)。
我想问的是在单线程中,D是否会阻塞,直到B执行完。 (2014-06-24 11:01) 

不好意思,多了个女
离线lbknxy

只看该作者 12楼 发表于: 2014-06-24
回 dbzhang800 的帖子
dbzhang800:其实,和物理学中的运动一样。在论坛中,错是绝对的,对是相对的。只有在在特定情况下,才好说什么是对的。不然,任何一个问题,追求严谨的话,都需要长篇大论了。
拿楼主的问题而言,可能隐含的特例之一是,他在槽函数内,直接emit其他信号,进而触发其他槽,这时,两个槽就是 .. (2014-06-24 09:47) 

恩恩,谢谢女版主
我想的情况是这样的:A信号触发了B槽,在B槽正在执行的过程中,外部事件(比如网络数据的到来)触发了信号C,这时C触发了D槽(D和B是两个不想关的槽)。
我想问的是在单线程中,D是否会阻塞,直到B执行完。
离线lbknxy

只看该作者 11楼 发表于: 2014-06-24
我是个新手,这两天才自学的Qt,想做个东西,就突然想到了这个问题,Qt手册上也没讲,所以就想问问大家,感觉大家都好厉害!
离线toby520

只看该作者 10楼 发表于: 2014-06-24
我表示围观不语,看大牛讨论技术,打的难解难分
QtQML多多指教开发社区 http://qtclub.heilqt.com
将QtCoding进行到底
关注移动互联网,关注金融
开发跨平台客户端,服务于金融行业
专业定制界面
群号:312125701   373955953(qml控件定做)
离线dbzhang800

只看该作者 9楼 发表于: 2014-06-24
回 cahwxy 的帖子
cahwxy:版主没有看清楚我的回复,我回答的很清楚的了
而版主又把物理牵涉进来,我感觉我连累了物理,哈哈
其实很简单的,程序的单线程并不是一定就按顺序执行,如定时器、网络访问等
....... (2014-06-24 10:13) 

一开始只是对你的回复困惑(还以为你想表述的是多线程中的某种特列),现在是真的不能理解了
离线cahwxy

只看该作者 8楼 发表于: 2014-06-24
版主没有看清楚我的回复,我回答的很清楚的了

而版主又把物理牵涉进来,我感觉我连累了物理,哈哈

其实很简单的,程序的单线程并不是一定就按顺序执行,如定时器、网络访问等

呵呵,版主没有明白我的意思
离线dbzhang800

只看该作者 7楼 发表于: 2014-06-24
回 cahwxy 的帖子
cahwxy:看样子版主的Qt实际经验也少的很呀 (2014-06-24 09:12) 

其实,和物理学中的运动一样。在论坛中,错是绝对的,对是相对的。只有在在特定情况下,才好说什么是对的。不然,任何一个问题,追求严谨的话,都需要长篇大论了。


拿楼主的问题而言,可能隐含的特例之一是,他在槽函数内,直接emit其他信号,进而触发其他槽,这时,两个槽就是嵌套执行的。但是,除非问题中明确指出,一般考虑常规情况。

所以我觉得其他人的回复没什么问题。但是你的回复,并不是直接针对的楼主的问题,而且也没有给出你的前提,所以我不做评判。


离线pxiao_xiao

只看该作者 6楼 发表于: 2014-06-24
信号和槽 在一个线程里 完全不用担心这个问题 重载event()也不会造成困扰
在多线程环境下 跨线程链接 注意链接方式
离线cahwxy

只看该作者 5楼 发表于: 2014-06-24
回 dbzhang800 的帖子
dbzhang800:我倒是觉得2楼说的没什么大问题,你说的却比较难理解一些。
简单说,单线程情况下,在一个槽函数执行期间,其他信号(楼主用的“其他事件”)是根本就不会被触发的。(当然,如果那个槽函数内执行的是事件循环,又是另一回事了) (2014-06-24 09:07) 

看样子版主的Qt实际经验也少的很呀
离线dbzhang800

只看该作者 4楼 发表于: 2014-06-24
回 cahwxy 的帖子
cahwxy:楼上 的回复不正确的,请不要误导楼主可好
多个信号连接相同的槽函数,只要该槽函数中不设置同步控制(如互斥对象),函数就不会阻塞的,函数体本身就是一份执行代码而已。
特别提醒一下:Singal和Slot在调用中可能被阻塞的,这个要看信号和槽的连接方式。 (2014-06-24 08:24) 

我倒是觉得2楼说的没什么大问题,你说的却比较难理解一些。

简单说,单线程情况下,在一个槽函数执行期间,其他信号(楼主用的“其他事件”)是根本就不会被触发的。(当然,如果那个槽函数内执行的是事件循环,又是另一回事了)
离线cahwxy

只看该作者 3楼 发表于: 2014-06-24
楼上 的回复不正确的,请不要误导楼主可好

多个信号连接相同的槽函数,只要该槽函数中不设置同步控制(如互斥对象),函数就不会阻塞的,函数体本身就是一份执行代码而已。

特别提醒一下:Singal和Slot在调用中可能被阻塞的,这个要看信号和槽的连接方式。
离线angelus

只看该作者 2楼 发表于: 2014-06-24
单线程是不存在这种困扰的,因为是顺序执行,在一个槽没处理完的时候,不会激发另一个槽程序,除非你的CPU在单线程可以使用影分身。

多线程也是一样的,使用直连会出你这个问题,使用队列不会的。
离线alexltr

只看该作者 1楼 发表于: 2014-06-23
自己写一个小程序试一下不就可以看到是什么结果啦。

这里有个例子可以参考一下

http://www.qtcn.org/bbs/read-htm-tid-55192-ds-1-page-2.html#150083
我不从事IT,只是喜欢Qt。
我不是程序员,只是与程序有缘。
我写程序,只是为了让工作变得简单有序!

                      ----  一个一直在入门的编程学习者
快速回复
限100 字节
 
上一个 下一个