繁体   English   中英

Qt:信号主线程

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM