• 8118阅读
  • 13回复

[提问]从UI线程给子线程发信号,为什么没有被执行槽函数? [复制链接]

上一主题 下一主题
离线stlcours
 

只看楼主 倒序阅读 楼主  发表于: 2016-09-30

我做了一个功能,允许用户删除某个目录下的全部文件,删除的时候有一个进度条对话框和一个按钮,允许用户中断这个过程。
MyDialog::OnCancel()
{
   emit SigCancel();   // 发信号给MyThread::OnStop()槽函数
}

MyThread::OnStop()
{
   // 会收到信号并执行这个函数
   this->m_stop = true;
   emit SigCancel(); // 再次发送信号。之所以要这样中转,而没有通过MyDialog直接发送给tool对象,是因为tool对象是run()函数里当场申请的对象,没有提前预知。
}

MyThread::run()
{
    MyTool tool;
    connect(this, signal(SigCancel()), &tool, slot(ChangeVar()));
    tool.DeleteAllFiles();
}

MyTool::ChangeVar()
{
   // 这个函数的目的是,改变这个类的某个bool变量,使这个类的DeleteAllFiles()能够早点跳出这个循环
   m_not_delete = true;
}

MyTool::DeleteAllFiles()
{
    for (i=0; i<files.count(); i++) {
       if (m_not_delete) break;
       // 真正每次删除一个文件
       files.at(i).delete();
    }
}

问题1:当用户按了这个按钮以后,会发信号到线程(MyThread)的槽里,然后线程的槽里再次发信号给某个类(MyTool)的槽函数。可问题是,我发现MyTool没有接收到这个信号。请问这是怎么回事?

问题2:UI线程发给子线程信号,即使子线程的槽函数接受到了,那么它应该何时执行这个槽函数的内容?因为子线程在永远不停的执行DeleteAllFiles();里的内容,循环往复,永远不停,那么子线程何时才会切换到执行槽函数的内容?我的问题1是不是因为这个原因造成的?

附带解释:子线程发信号给UI线程,很容易,很好用,我已经实现使用了。因为即使UI线程有可能在忙,但总有停下来的时候(因为UI线程大部分时间都在等人来操作),但子线程却不是这样,有可能永远在工作,一旦工作完毕就有可能彻底退出和销毁了线程。所以UI线程给工作线程发信号,是一个很奇怪的问题。
离线mu_de_yu

只看该作者 1楼 发表于: 2016-09-30
你这样写,不闲的很麻烦吗,来回发信号,我建议你为何不把DeleteAllFiles()功能改为删除一个文件功能。然后在run里循环调用,达到删除所有文件的目的。run 这样写:
while(1){
    if(flag_stop_delete){
      // 在这里执行删除单个文件函数
    }
     ... ...
     QThread::sleep(1);
}
当你的OnStop()响应时,在里面将flag_stop_delete置为false即可,删除功能就不再执行
离线mu_de_yu

只看该作者 2楼 发表于: 2016-09-30
针对你的问题:
两个问题起始是一个问题:
信号槽响应也是正常的函数调用,遵循事件队列先后执行,你的子线程在删除文件(一直循环),信号发送和槽函数响应也是你子线程执行的,所以只会在你循环结束后才会调用
离线dbzhang800

只看该作者 3楼 发表于: 2016-09-30
你的用法是错误的,它不可能工作的。

首先:你的 connect 用的是 Qt::AutoConnection 方式。
其次:你需要知道,你的MyThread::OnStop()是在主线程执行,它里面的信号肯定就是在主线程
再次:你的tool对象在次线程创建,与信号不在同一个线程。所以你的connect此时等效于 Qt::QueuedConnection
再再次:你的次线程内压根没有事件循环,信号对应的事件不可能被次线程收到。更没办法让事件系统调用你的槽函数了。
离线stlcours

只看该作者 4楼 发表于: 2016-09-30
回 dbzhang800 的帖子
dbzhang800:你的用法是错误的,它不可能工作的。
首先:你的 connect 用的是 Qt::AutoConnection 方式。
其次:你需要知道,你的MyThread::OnStop()是在主线程执行,它里面的信号肯定就是在主线程
再次:你的tool对象在次线程创建,与信号不在同一个线程。所以你的connect此时等效于 Qt::Que .. (2016-09-30 10:23) 

请问你说的次线程没有事件循环队列,是指什么?应该怎么加上?
没有事件循环队列就不能使用信号槽吗?我在这个次线程tool里使用emit调用主线程的对话框没有任何问题啊。
离线stlcours

只看该作者 5楼 发表于: 2016-09-30
回 mu_de_yu 的帖子
mu_de_yu:你这样写,不闲的很麻烦吗,来回发信号,我建议你为何不把DeleteAllFiles()功能改为删除一个文件功能。然后在run里循环调用,达到删除所有文件的目的。run 这样写:
while(1){
    if(flag_stop_delete){
      // 在这里执行 .. (2016-09-30 09:30) 

你这个回答很简洁有力。但我是把问题模型简化了的。MyTool类里,有一堆的相关函数需要调用。最好把这些功能都写在MyTool类里。如果实在没办法,也就只有把那部分代码单独拿出来放到run里了。
离线stlcours

只看该作者 6楼 发表于: 2016-09-30
回 mu_de_yu 的帖子
mu_de_yu:针对你的问题:
两个问题起始是一个问题:
信号槽响应也是正常的函数调用,遵循事件队列先后执行,你的子线程在删除文件(一直循环),信号发送和槽函数响应也是你子线程执行的,所以只会在你循环结束后才会调用
 (2016-09-30 09:39) 

你这个回答,也是直击要害。就是子线程在删除文件(一直循环)结束之前,是不会接收到信号的,我也是这样认为的。但即使循环结束以后,子线程就能收到信号吗?我下断点之后,没有发现程序跳到MyTool::ChangeVar()中来。也许是因为DeleteAllFiles就是run()的最后内容了,所以线程被销毁了,没来及执行MyTool::ChangeVar()槽函数。要不我在run()执行DeleteAllFiles()之后,再加上一个其它的费时操作,这样中间就有了空隙,看看能不能执行到这个ChangeVar()槽函数。
离线mu_de_yu

只看该作者 7楼 发表于: 2016-09-30
回 stlcours 的帖子
stlcours:你这个回答很简洁有力。但我是把问题模型简化了的。MyTool类里,有一堆的相关函数需要调用。最好把这些功能都写在MyTool类里。如果实在没办法,也就只有把那部分代码单独拿出来放到run里了。 (2016-09-30 15:45) 

1.就删除个文件,执行一条命令就行,还有一堆相关操作?
难道你将进度条显示也放到MyTool类里?
(如果是,理论上是行不通的,UI控件类只能在GUI主线程跑)
2.你的运行环境是在WIN还是Linux下?
离线stlcours

只看该作者 8楼 发表于: 2016-09-30
回 mu_de_yu 的帖子
mu_de_yu:1.就删除个文件,执行一条命令就行,还有一堆相关操作?
难道你将进度条显示也放到MyTool类里?
(如果是,理论上是行不通的,UI控件类只能在GUI主线程跑)
2.你的运行环境是在WIN还是Linux下? (2016-09-30 16:18) 

其实是删除网络上的文件,不是本地文件,第三方库要初始化+验证等等,所以要一堆相关操作。另外这个删除所有文件的操作,是有一些类都会使用的,所以放在MyTool里归类比较好。
我运行在WIN。但最终结果要发布到三个平台。

另外请回答一下我的另一个问题,就是何时执行MyTool::ChangeVar()的问题
离线dbzhang800

只看该作者 9楼 发表于: 2016-09-30
要使得你的ChangeVar槽能执行

要么:connect 使用 Qt::DirectConnection
要么:run 内开启事件循环QThread::exec()
离线mu_de_yu

只看该作者 10楼 发表于: 2016-09-30
回 stlcours 的帖子
stlcours:其实是删除网络上的文件,不是本地文件,第三方库要初始化+验证等等,所以要一堆相关操作。另外这个删除所有文件的操作,是有一些类都会使用的,所以放在MyTool里归类比较好。
我运行在WIN。但最终结果要发布到三个平台。
另外请回答一下我的另一个问题,就是何时执行MyTool::Ch .. (2016-09-30 16:26) 

哦,好吧,对于你这个问题,你可以试下用全局变量
1.将你那个Bool变量设成全局
2.在OnStop()里将Bool变量设成false(记得加锁)
3.在删除文件函数里,每次删一个文件前,先判断Bool变量的值,为true则删,反之则跳出循环
恩,我对网络编程这块不熟,此方法只做参考
离线stlcours

只看该作者 11楼 发表于: 2016-09-30
回 dbzhang800 的帖子
dbzhang800:要使得你的ChangeVar槽能执行
要么:connect 使用 Qt::DirectConnection
要么:run 内开启事件循环QThread::exec() (2016-09-30 16:37) 

谢谢。但是第二种方法的exec()应该写在哪里呢?它不是会阻塞整个线程吗?难道还要等到DeleteAllFiles()执行结束的时候,再发信号要求结束这个exec()?这样太麻烦了啊。

离线stlcours

只看该作者 12楼 发表于: 2016-10-01
回 dbzhang800 的帖子
dbzhang800:要使得你的ChangeVar槽能执行
要么:connect 使用 Qt::DirectConnection
要么:run 内开启事件循环QThread::exec() (2016-09-30 16:37) 

你的第一个方法,亲测有效。但是我觉得有点不太好理解,跨线程调用也可以强行指定DIRECT方式吗?QT官方自己却是使用排队方法呢。。。
离线alvinlmf

只看该作者 13楼 发表于: 2016-10-13
我只能说你对线程的理解是错误的,线程一般官方推荐就两个方法:
1.在run()方法里面执行你所谓的循环
2.用moveToThread()这个方法
对于线程的这两种方法,你可以仔细看看
奋斗不止
快速回复
限100 字节
 
上一个 下一个