[英]Qt: Signal main thread
存在许多类似的问题,但没有找到合适的答案。
我使用第三方库。
当一些对LIB的类的虚方法被调用,这些是从尚未启动我的应用程序中的工作线程调用。 这个帖子不是QThread,也不是。
我可以从这个线程发出,但只有当我使用Qt :: DirectConnection连接插槽时。 结果是SLOT中的QObject :: sender()将始终返回NULL。 我希望调用deleteLater(),但是只能在QThread中调度。
我想我需要回到主线程,但是如何在主线程上发出信号呢?
示例:当调用以下方法时,它是在第三方库创建的线程上完成的。
/*virtual*/ bool MediaPlayer::onEof()
{
stopTransmit();
emit sigFinished(); // slots only called if bound using Qt::DirectConnection
deleteLater(); // dtor is never called
return false;
}
连接也是在非QThread上下文中进行的,如下所示:
/*virtual*/ void
SipCall::state_answer_call::onEntering(SipCall& ref)
{
...
MediaPlayer* player = new MediaPlayer;
ref.connect(player, SIGNAL(sigFinished()), SLOT(slotMediaFinished()), Qt::DirectConnection);
...
}
如果没有显式的Qt::DirectConnection
,则永远不会调用SipCall::slotMediaFinished()
。
问题是您的MediaPlayer
实例player
是在没有活动事件循环的线程上创建的。
当您将代码更改为...
ref.connect(player, SIGNAL(sigFinished()), SLOT(slotMediaFinished()), Qt::QueuedConnection);
Qt基础结构将事件发布到与player
关联的线程。 由于没有事件循环来处理该事件,因此永远不会调用目标代码MediaPlayer::slotMediaFinished
。
所以问题是......你希望调用哪个线程MediaPlayer::slotMediaFinished
? 如果它是您可以尝试的主应用程序线程(假设您使用的是qt5和c ++ 11)...
ref.connect(player, &MediaPlayer::sigFinished, QCoreApplication::instance(),
[&]()
{
player->slotMediaFinished();
},
Qt::QueuedConnection);
这将使用与QCoreApplication
实例关联的QThread
作为执行lambda的上下文 。
编辑:
正如你所说,你不能使用qt5或c ++ 11我唯一可以建议的另一个选择是在你的主应用程序线程上有一个QObject
派生变量,可以作为player
的代理上下文......
class proxy: public QObject {
Q_OBJECT;
public slots:
void slotMediaFinished ()
{
if (MediaPlayer *player = dynamic_cast<MediaPlayer *>(sender()) {
player->slotMediaFinished();
}
}
};
在您的应用程序线程上创建上述实例,然后您的connect
语句将是......
ref.connect(player, SIGNAL(sigFinished()), &proxy_instance, SLOT(slotMediaFinished()), Qt::QueuedConnection)
其中proxy_instance
是呃proxy
实例。 现在,当发出sigFinished
信号时,Qt会将事件发布到proxy_instance
。 反过来,它将调用proxy::slotMediaFinished
,它可以从QObject::sender
识别感兴趣的MediaPlayer
实例并调用其slotMediaFinished
成员。
当调用lib类的一些虚方法时,这些方法是从我的应用程序尚未启动的工作线程调用的。 这个帖子不是QThread,也不是。
有一个错误的信念,认为这是一个问题。 它不是。 发出信号的线程并不重要。 它不必使用QThread
启动。
实际上,从C回调发出信号是将多线程C回调API连接到Qt的惯用方法。 它意味着不需要任何努力。
我可以从这个线程发出,但只有当我使用Qt :: DirectConnection连接插槽时。
这不是真的。 当信号和插槽位于不同的线程中时,如果连接的插槽/仿函数是线程安全的,则只能使用直接连接。 我怀疑你是,那么你不应该使用直接连接。 自动连接将完全满足您的需求。
它不起作用,因为接收SipCall
实例不存在于具有正在运行的事件循环的线程中。 您必须将其移动到这样的线程,或者使用生成在主线程中的thunk仿函数,如GM对此问题的回答中所建议的那样。
thunk仿函数的问题在于它们模糊了你正在调用方法的对象的线程所有权。 最有可能的SipCall
方法不是线程安全的要么 ,所以从主线程中调用这些你一定会打破东西。 将播放器移动到应用程序线程是最安全的,并确保它仅在该线程中使用。 那么你就不需要thunk functors了。
如果您想了解在给定线程中运行某些代码(例如仿函数)的方法,请参阅此问题 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.