2011-03-10 20 views
2

在我的節目,我是子類的QThread開始了,我實現了虛擬方法run()像這樣:知道當的QThread的事件循環已經從另一個線程

void ManagerThread::run() { 

    // do a bunch of stuff, 
    // create some objects that should be handled by this thread 
    // connect a few signals/slots on the objects using QueuedConnection 

    this->exec(); // start event loop 
} 

現在,在另一個線程(我們稱之爲MainThread ),我開始ManagerThread等待它的started()信號,之後我繼續使用應該由ManagerThread處理的信號和時隙。然而,started()信號基本上emmitted run()被稱爲權之前,讓取決於線程調度我失去了從MainThread一些信號,因爲事件循環還沒有開始!

編輯:原來,這不是問題,它只是將信號在時間上不相連,但出於同樣的原因)

我能正確調用exec()之前發出的信號,但是這也要求爲了麻煩。

是否有任何明確/簡單的方式知道事件循環已經開始?

謝謝!

EDIT2:(液)

好了,所以原來的問題是不正是我措辭。事件循環未開始的事實不是問題,因爲應該排隊,直到它開始。問題是,一些信號會不會被連接的時間,因爲run()被稱爲前started()信號被髮射是called-。

解決方案在所有連接之後並且在exec之前發出另一個自定義信號。這樣所有的信號/插槽都可以確保連接。

這是我的問題的解決方案,但並不真正解決主題標題問題。我已經接受了確實回答題目的答案。

我已經離開我的代碼下面那些好奇,與溶液中,等待在instance()方法的另一個信號。


CODE:

很多的你是說我不能失去信號,所以這裏是我的整個類的實現。我會簡化它到最基本的必需品。

這裏是接口ManagerThread

// singleton class 
class ManagerThread: public QThread { 

    Q_OBJECT 

    // trivial private constructor/destructor 

    public: 
    static ManagerThread* instance(); 

    // called from another thread 
    public: 
    void doSomething(QString const& text); 

    // emitted by doSomething, 
    // connected to JobHandler whose affinity is this thread. 
    signals: 
    void requestSomething(QString const& text); 

    // reimplemented virtual functions of QThread 
    public: 
    void run(); 

    private: 
    static QMutex s_creationMutex; 
    static ManagerThread* s_instance; 
    JobHandler* m_handler; // actually handles the requests 
}; 

一些相關的實現。創建線程的單一實例:

ManagerThread* ManagerThread::instance() { 
    QMutexLocker locker(&s_creationMutex); 
    if (!s_instance) { 
     // start socket manager thread, and wait for it to finish starting 
     s_instance = new ManagerThread(); 

     // SignalWaiter essentially does what is outlined here: 
     // http://stackoverflow.com/questions/3052192/waiting-for-a-signal 
     SignalWaiter waiter(s_instance, SIGNAL(started())); 
     s_instance->start(QThread::LowPriority); 
     qDebug() << "Waiting for ManagerThread to start"; 
     waiter.wait(); 
     qDebug() << "Finished waiting for ManagerThread thread to start."; 
    } 

    return s_instance; 
} 

運行重新實現,設置了信號/插槽和啓動事件循環:

void ManagerThread::run() { 
    // we are now in the ManagerThread thread, so create the handler 
    m_handler = new JobHandler(); 

    // connect signals/slots 
    QObject::connect(this, 
        SIGNAL(requestSomething(QString const&)), 
        m_handler, 
        SLOT(handleSomething(QString const&)), 
        Qt::QueuedConnection); 

    qDebug() << "Starting Event Loop in ManagerThread"; 

    // SOLUTION: Emit signal here and wait for this one instead of started() 

    this->exec(); // start event loop 
} 

功能是委託處理,以正確的線程。這是 我發出對丟失的信號:

void ManagerThread::doSomething(QString const& text) { 

    qDebug() << "ManagerThread attempting to do something"; 

    // if calling from another thread, have to emit signal 
    if (QThread::currentThread() != this) { 
     // I put this sleep here to demonstrate the problem 
     // If it is removed there is a large chance the event loop 
     // will not start up in time to handle the subsequent signal 
     QThread::msleep(2000); 
     emit(requestSomething(text)); 
    } else { 
     // just call directly if we are already in the correct thread 
     m_handler->handleSomething(text); 
    } 
} 

最後,這裏是從MainThread的代碼,如果事件循環沒有及時啓動會失敗:

ManagerThread::instance()->doSomething("BLAM!"); 

假設處理程序只是打印出它的文本,以下是打印成功運行的內容:

W請等待ManagerThread線程啓動
已完成等待ManagerThread線程啓動。
ManagerThread中啓動事件循環
ManagerThread嘗試做某事
BLAM!

這裏是一個不成功的運行會發生什麼:

等待ManagerThread開始
完畢等待ManagerThread線程啓動。
ManagerThread試圖做一些事情
在ManagerThread

起事件循環很明顯,事件循環開始信號發出了之後,和BLAM從不打印。 這裏有一個競爭條件,需要知道事件循環何時開始, 爲了解決它。

也許我失去了一些東西,問題是不同的東西......

感謝這麼多,如果你真的閱讀所有!唷!

+1

你確定你失去了信號?如果信號從線程內部連接,則它們被設置爲排隊信號。如果它們沒有線程連接,則它們是同步信號。查看該場景的其餘部分以查看信號如何/何時連接會很有幫助。 – lefticus 2011-03-10 15:51:09

+0

@lefticus我已使用所需的代碼更新了我的帖子以查看問題。最後,我包含了兩個程序的輸出。一個成功(事件循環時間創建),另一個失敗(在事件循環創建之前信號丟失)。 – 2011-03-10 18:26:20

回答

3

如果您設置的連接權,你不應該失去信號。但是,如果您確實想要注意線程事件循環的開始,您可以在致電exec()之前在run()之前嘗試QTimer::singleShot()。它將在事件循環開始時交付並且僅交付一次。

+0

事實證明,我的問題並不完全是我問的,所以我不需要知道事件循環何時開始。但是,既然這是問題的標題,這將是正確的答案。特別是'QTimer :: singleShot()'可以發出一個信號,應該等待。當線程進入事件循環時,處理'QTimer',觸發信號,並且我們知道事件循環已經開始!謝謝! – 2011-03-10 19:13:11

1

您可以查看QSemaphore以在線程之間發出信號。插槽和信號對於同一線程中的UI事件和回調更好。

編輯:或者,如果信號量不適用,您可以將QMutexQWaitCondition結合。查看如何結合MainThread使用ManagerThread的更多示例代碼會很有幫助。

+1

我可以試着在MainThread中的信號量上獲取()等待,但是問題是,什麼會在信號量上調用'release()'來告訴我'ManagerThread'的事件循環已開始? – 2011-03-10 16:01:35

+0

'connect()'方法是線程安全的,但是否則從'QObject'派生的對象不是你可能需要某種鎖定機制,無論在'connect()中使用'Qt :: QueuedConnection' ......爲什麼投票? – AJG85 2011-03-10 16:19:53

+0

任何並行編程都需要了解數據同步。但是,有多種情況下您不需要鎖定數據對象本身(流水線模型)。 QT中的信號/插槽機制恰好提供了跨線程,因爲它是一個線程安全隊列。使用它們的跨線程通信也很容易,因爲它又是一個線程安全隊列。坦率地說,不要使用它,尤其是考慮到整個框架是圍繞它們而建的。 – 2011-03-10 16:36:27

1

這是一個非問題。線程之間的信號排隊(更具體地說,您需要將它們設置爲在connect()調用中排隊,因爲線程之間的直接連接不安全)。

http://doc.qt.io/qt-5/threads-qobject.html#signals-and-slots-across-threads

+0

如果連接發生在線程之間,Auto(默認)會選擇排隊的連接,如您指向的文檔所示。 – lefticus 2011-03-10 16:51:36

+0

謝謝 - 我已經在QT工作了一段時間。雖然我認爲我總是明確地設置它,原因很簡單,當我查看代碼時,我確切知道它在做什麼。 – 2011-03-10 17:06:20

+0

@Brian Roach我已經發布了有問題的代碼,並且所有證據都表明信號在事件循環開始之前丟失。感謝您的關注。 – 2011-03-10 18:16:12

0

您可以在ManagerThread的構造函數中創建信號/插槽連接。這樣,即使在run()被調用之前,它們也是連接的。

+0

問題是我需要在'ManagerThread'線程中創建'JobHandler'(注意在類的方法中創建的區別與特定線程內的實際創建之間的區別)。因此,我需要在'run()'方法內創建'JobHandler'實例,該方法將在正確的線程中運行。我無法在構造函數中建立連接,因爲'JobHandler'實例尚不存在。 – 2011-03-10 18:23:52

+0

@Alexander不能使用MoveToThread將JobHandler移動到線程啓動時? – 2011-03-10 18:26:04

+0

我想這將是可能的。問題已經解決,謝謝你的建議。 – 2011-03-10 20:59:26

相關問題