简体   繁体   English

Qt:信号主线程

[英]Qt: Signal main thread

Lots of similar questions exists, but no suitable answer found. 存在许多类似的问题,但没有找到合适的答案。

I use a 3rd party library. 我使用第三方库。

When some of the virtual methods on the lib's classes are called, these are called from a worker thread that has not been started by my application. 当一些对LIB的类的虚方法被调用,这些是从尚未启动我的应用程序中的工作线程调用。 This thread is not a QThread, nor could it ever be. 这个帖子不是QThread,也不是。

I can emit from this thread, but only if I connect the slot using Qt::DirectConnection. 我可以从这个线程发出,但只有当我使用Qt :: DirectConnection连接插槽时。 The upshot is that QObject::sender() in the SLOT will always return NULL. 结果是SLOT中的QObject :: sender()将始终返回NULL。 I wish to call deleteLater() for instance, but that can only be scheduled in a QThread. 我希望调用deleteLater(),但是只能在QThread中调度。

I think I need to get back to the main thread, but how can I signal the object on the main thread? 我想我需要回到主线程,但是如何在主线程上发出信号呢?

Example: when the below method is called, it is done so on a thread created by 3rd party library. 示例:当调用以下方法时,它是在第三方库创建的线程上完成的。

/*virtual*/ bool MediaPlayer::onEof()
{
  stopTransmit();
  emit sigFinished();  // slots only called if bound using Qt::DirectConnection
  deleteLater();       // dtor is never called

  return false;
}

The connection is made from within a non-QThread context also, as follows: 连接也是在非QThread上下文中进行的,如下所示:

/*virtual*/ void
SipCall::state_answer_call::onEntering(SipCall& ref)
{
  ...
  MediaPlayer* player = new MediaPlayer;
  ref.connect(player, SIGNAL(sigFinished()), SLOT(slotMediaFinished()), Qt::DirectConnection);
  ...
}

Without the explicit Qt::DirectConnection , SipCall::slotMediaFinished() is never invoked. 如果没有显式的Qt::DirectConnection ,则永远不会调用SipCall::slotMediaFinished()

The problem is that your MediaPlayer instance player is created on a thread that doesn't have an active event loop. 问题是您的MediaPlayer实例player是在没有活动事件循环的线程上创建的。

When you change the code to... 当您将代码更改为...

ref.connect(player, SIGNAL(sigFinished()), SLOT(slotMediaFinished()), Qt::QueuedConnection);

the Qt infrastructure will post an event to the thread associated with player . Qt基础结构将事件发布到与player关联的线程。 As there is no event loop to process that event the target code MediaPlayer::slotMediaFinished will never be called. 由于没有事件循环来处理该事件,因此永远不会调用目标代码MediaPlayer::slotMediaFinished

So the question is... which thread do you want MediaPlayer::slotMediaFinished to be called on? 所以问题是......你希望调用哪个线程MediaPlayer::slotMediaFinished If it's the main application thread you could try (assuming you're using qt5 and c++11)... 如果它是您可以尝试的主应用程序线程(假设您使用的是qt5和c ++ 11)...

ref.connect(player, &MediaPlayer::sigFinished, QCoreApplication::instance(),
  [&]()
  {
    player->slotMediaFinished();
  },
  Qt::QueuedConnection);

That will use the QThread associated with the QCoreApplication instance as a context in which to execute the lambda. 这将使用与QCoreApplication实例关联的QThread作为执行lambda的上下文

Edit: 编辑:

As you've stated you can't use qt5 or c++11 the only other option I can suggest is to have a QObject derived variable on your main application thread that can act as a proxy context for player ... 正如你所说,你不能使用qt5或c ++ 11我唯一可以建议的另一个选择是在你的主应用程序线程上有一个QObject派生变量,可以作为player的代理上下文......

class proxy: public QObject {
  Q_OBJECT;
public slots:
  void slotMediaFinished ()
    {
      if (MediaPlayer *player = dynamic_cast<MediaPlayer *>(sender()) {
        player->slotMediaFinished();
      }
    }
};

Create an instance of the above on your application thread and then your connect statement would be... 在您的应用程序线程上创建上述实例,然后您的connect语句将是......

ref.connect(player, SIGNAL(sigFinished()), &proxy_instance, SLOT(slotMediaFinished()), Qt::QueuedConnection)

Where proxy_instance the, er, proxy instance. 其中proxy_instance是呃proxy实例。 Now, when the sigFinished signal is emitted Qt will post the event to proxy_instance . 现在,当发出sigFinished信号时,Qt会将事件发布到proxy_instance That in turn will call proxy::slotMediaFinished which can identify the MediaPlayer instance of interest from QObject::sender and invoke its slotMediaFinished member. 反过来,它将调用proxy::slotMediaFinished ,它可以从QObject::sender识别感兴趣的MediaPlayer实例并调用其slotMediaFinished成员。

When some of the virtual methods on the lib's classes are called, these are called from a worker thread that has not been started by my application. 当调用lib类的一些虚方法时,这些方法是从我的应用程序尚未启动的工作线程调用的。 This thread is not a QThread, nor could it ever be. 这个帖子不是QThread,也不是。

There's a mistaken belief that it's a problem. 有一个错误的信念,认为这是一个问题。 It is not. 它不是。 The thread where you emit the signals is immaterial. 发出信号的线程并不重要。 It doesn't have to be started using QThread . 它不必使用QThread启动。

In fact, emitting a signal from a C callback is an idiomatic way of interfacing multithreaded C callback APIs to Qt. 实际上,从C回调发出信号是将多线程C回调API连接到Qt的惯用方法。 It is meant to work without any effort. 它意味着不需要任何努力。

I can emit from this thread, but only if I connect the slot using Qt::DirectConnection. 我可以从这个线程发出,但只有当我使用Qt :: DirectConnection连接插槽时。

That's not true. 这不是真的。 When the signals and slots live in different threads, you can only use direct connections if the slots/functors you connect to are thread safe. 当信号和插槽位于不同的线程中时,如果连接的插槽/仿函数是线程安全的,则只能使用直接连接。 I doubt yours are, so you should not use direct connection. 我怀疑你是,那么你应该使用直接连接。 The automatic connection will do exactly what you need. 自动连接将完全满足您的需求。

It doesn't work because the receiving SipCall instance doesn't live in a thread with a running event loop. 它不起作用,因为接收SipCall实例不存在于具有正在运行的事件循环的线程中。 You'll have to either move it to such a thread, or use a thunk functor living in the main thread as suggested in the GM's answer to this question . 您必须将其移动到这样的线程,或者使用生成在主线程中的thunk仿函数,如GM对此问题的回答中所建议的那样。

The problem with thunk functors is that they obfuscate thread ownership of the object you're invoking the methods on. thunk仿函数的问题在于它们模糊了你正在调用方法的对象的线程所有权。 Most likely the SipCall methods aren't thread-safe either , so by calling them from the main thread you're bound to break things. 最有可能的SipCall方法不是线程安全的要么 ,所以从主线程中调用这些你一定会打破东西。 It's safest to move the player to the application thread, and ensure it's only used from that thread. 将播放器移动到应用程序线程是最安全的,并确保它仅在该线程中使用。 You won't need thunk functors then. 那么你就不需要thunk functors了。

If you want to see what ways there are of running some code (eg a functor) in a given thread, see this question . 如果您想了解在给定线程中运行某些代码(例如仿函数)的方法,请参阅此问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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