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