关于QTimer和线程一个奇葩争论,不知道怎么解决

qq_21042359 2020-08-07 10:12:19
假如这里有2个人,1个人写Qobject的类,一个人写主线程UI。好,我第一个人封装了一个Obj,他的构造函数是这样的:

ClassA::ClassA(QObject *parent) : QObject(parent)
{
ti = new QTimer(this);
ti->setInterval(1000);
connect(ti, SIGNAL(timeout()), this, SLOT(onTimeut()));
ti->start();
}

他觉得我自己的timer就是定时触发我一个槽函数,而且这个timer是我的,所以把timer的parent指向自己,这样子自己被析构的时候,会一起把timer也析构掉。析构函数都可以懒得写了,没问题。

另外一个人写主线程:

ClassA *a = new ClassA();
QThread *th = new QThread();
a->movetoThread(th);
th->start();

这个人不知道这个Class A里实现什么,他也不需要知道,只需要按照语法规则new a,然后后面delete a就对了,但是这样delete的时候,QTimer就是报错,QObject::killTimer: timers cannot be stopped from another thread。
哦对了,如果你觉得写主线程的人应该先退出子线程,那很可惜,你这么写一样报错。

th->quit();
th->wait();
th->deletelater();
delete a;


那么请问:2个人都觉得自己合情合理,没有问题,那到底是谁的问题?有什么通用的方法或者思想来避免这种问题。
...全文
28466 9 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq_21042359 2020-08-31
  • 打赏
  • 举报
回复
引用 7 楼 氺月洞天 的回复:
接5楼,不同线程的,不能直接delete对方的obj,只能通过信号槽来实现跨线程的delete
那是只能某信号->这个obj的deletelater(),这样子?但是这里有个问题,不够完整。假如这个obj的线程先被退出了,那么这个obj是不会执行事件的,也就是槽函数不会执行呢
qq_21042359 2020-08-31
  • 打赏
  • 举报
回复
引用 7 楼 氺月洞天 的回复:
接5楼,不同线程的,不能直接delete对方的obj,只能通过信号槽来实现跨线程的delete
嗯?换句话说,假如把一个obj通过move来移动到子线程,就必须要用信号槽来delete吗?
氺月洞天 2020-08-28
  • 打赏
  • 举报
回复
接5楼,不同线程的,不能直接delete对方的obj,只能通过信号槽来实现跨线程的delete
王桑的一天 2020-08-20
  • 打赏
  • 举报
回复
我觉得 moveToThread() 这种方法好麻烦,还要连接 started 信号 与槽函数,不如继承 QThread 单干一件事多好
Jonix 2020-08-12
  • 打赏
  • 举报
回复
如果一定要说是谁的问题,肯定是第一个人的问题。他没有考虑到这个类会被装入到另一个线程中运行。所以 new 和 delete 不在同一个线程中了
qq_21042359 2020-08-11
  • 打赏
  • 举报
回复
引用 3 楼 芒果黑 的回复:
不用QTimer用timerEvent就不会出现这种情况了吧
这个你可以试一下,但并不是在我讨论的范围里,我讨论的是我正常使用一个东西,我只认qt和c++的规则,其他什么同事之间呀,网友之间的“自定义规则”全部不认,但我必须在仅有的2种规则下,不允许出错。就是说,谁都不允许搞特殊,如果出现某个东西搞特殊,那就是严重破坏封装性和设计模式规则。我能用timer为啥非得搞特殊用timerevent。
芒果黑 2020-08-10
  • 打赏
  • 举报
回复
不用QTimer用timerEvent就不会出现这种情况了吧
qq_21042359 2020-08-07
  • 打赏
  • 举报
回复
总结一下: 1.Obj里的其他Obj,包括QTimer,指定了parent为自己this的话,别人在外面moveToThread会把你的timer的线程也移动过去。 2.如果别人在外面delete你的Obj,那么你的Timer析构时候调用的stop(),里面判断thread() != QThread::currentThread()就成立了,然后就报出警告qWarring啦。 3.deletelater是postEvent的,也就是析构会在槽函数,也就是在子线程里,这个或许是唯一解了,毕竟你的timer的析构的stop()要在子线程干的呢。 4.moveToThread只能把Obj推去子线程,不能拉回主线程。 5.那就是说,timer的stop时候,timer的thread()要完好无损,不能被删掉了哦,然后执行stop()的这个线程,不能是其他线程呢,所以就要信号槽一石二鸟,在th->wait()这个函数里面,会等待,然后就会发送信号,然后timer啊ta啊就会在子线程里被析构掉咯。然后线程自己也析构掉了。
qq_21042359 2020-08-07
  • 打赏
  • 举报
回复
经过反复的测试,我发现这样居然是唯一解?必须要信号槽,不能手动quit wait delete。主要几个关键点: 对于ClassA这个类来说,关键在于构造的时候new QTimer是传了个parent指定了ClassA,那么别人把ClassA移到新线程时候,timer会跟着一起到新的线程里面。那么在delete时候,delete执行的那个线程,不用信号槽,大概率是在主线程去删除,那么这个时候,timer->thread()和currentThread()就不是同一个线程,就会报错。如果先把子线程quit wait delete掉,那么timer和ClassA的thread()就是NULL,一样报错。 那么能不能在删除子线程后,把ClassA再拖回主线程呢?很可惜,QT说明文档写明了,moveToThread只能把Obj“推出去”,不能“拉回来”。那么能不能子线程先来一套quit() wait() deletelater(),然后再ClassA->deletelater()呢,也不行,因为ClassA的thread()已经是空了,deletelater实际上是postEvent的,也就是说ClassA是不会处理这个事件的。你们可以试一下,析构函数并不会被调用,而且这是灾难的。

16,814

社区成员

发帖
与我相关
我的任务
社区描述
Qt 是一个跨平台应用程序框架。通过使用 Qt,您可以一次性开发应用程序和用户界面,然后将其部署到多个桌面和嵌入式操作系统,而无需重复编写源代码。
社区管理员
  • Qt
  • 亭台六七座
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧