簡體   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