2016-10-14 56 views
0

我正在QT框架中的數據記錄器上工作。我打算將日誌字符串保存到文件並在單獨的觀察器線程中打印到控制檯。在那個單獨的線程中,我需要爲添加的新項目觀察我的QStringList。如果有新項目,我將它們排序並登錄。我想知道在Qt框架中使用什麼機制。在STD LIB我使用condition_variable這個任務是這樣的:觀察QStringList的新項目

/*! 
* \brief puts a \ref logline_t object at the end of the queue 
* @param s object to be added to queue 
*/ 
void CLogger::push_back(logline_t* s) 
{ 
    unique_lock<mutex> ul(m_mutex2); 
    s->queSize = m_data.size(); 
    m_data.emplace_back(move(s)); 
    m_cv.notify_all(); 
} 

/*! 
* \brief takes a \ref logline_t object from the beggining of the queue 
* @return first \ref logline_t object 
*/ 
CLogger::logline_t CLogger::pop_front() 
{ 
    unique_lock<mutex> ul(m_mutex2); 
    m_cv.wait(ul, [this]() { return !m_data.empty(); }); 

    assert(m_data.front()); 
    logline_t retVal = move(*m_data.front()); 

    delete m_data.front(); 
    m_data.front() = NULL; 

    m_data.pop_front(); 

    return retVal; 
} 

m_cv是條件可變對象。 QT如何獲得這個功能?我敢打賭有更多更好的方法:)。我將不勝感激所有幫助。 Ps:我知道指針函數的參數沒有被聲明,這是一箇舊代碼...:P

+2

您可以使用標準庫/使用Qt進行提升,使用條件變量沒有任何問題。但如果你想要全部使用Qt,那麼使用信號和插槽來實現這一點。不過,這可能會變慢。你必須進行基準測試。 – krzaq

+0

我不想觸摸這個提升。就像你說的,我想看看Qt在這裏給出了一些不錯的東西。有了信號/插槽,即使它起初看起來很明顯,但我不知道這是否是這種事情的正確選擇。我並不是說只有gui纔會使用信號/插槽,但也許我認爲qt框架中有一些專用的監視器,或者類似於freeRTOS提供的東西(當您將項目添加到隊列中時,正在解鎖等待任務)。另外我不確定信號/插槽是否較慢,總是有爭論。 – Bremen

+2

如果你不過分關注性能(儘管它們並不可怕),信號和插槽可以實現很好的同步,無論是否是gui。我使用它們在生產中的線程之間傳遞消息(控制檯應用程序作爲服務運行)多年,現在它們很好。你沒有得到先入先出的保證,但實際上你會得到這個。如果有另一個來自Qt的同步api,那麼我不知道它。 – krzaq

回答

1

下面是做這個信號和插槽的一個例子。你可能想要做自己的基準來測試這是否符合你的需求。另請注意,儘管信號和插槽可確保線程安全,但它們不保證消息將按照發送的順序顯示。話雖如此,我已經使用了這種機制多年,它還沒有發生。

首先,創建幾個類:

Loggee

// loggee.hpp 
#ifndef LOGGEE_HPP 
#define LOGGEE_HPP 

#include <QObject> 

class Loggee : public QObject 
{ 
    Q_OBJECT 
public: 
    using QObject::QObject; 


    void someEventHappened(int id); 

signals: 

    void newLogLineNotify(QString const&); 
}; 

#endif // LOGGEE_HPP 

和.cpp文件:

#include "loggee.hpp" 

void Loggee::someEventHappened(int id) 
{ 
    auto toLog = QString::number(id); 
    emit newLogLineNotify(toLog); 
} 

記錄儀

#ifndef LOGGER_HPP 
#define LOGGER_HPP 

#include <QObject> 

class Logger : public QObject 
{ 
    Q_OBJECT 
public: 
    using QObject::QObject; 

signals: 

public slots: 

    void onLogEventHappened(QString const&); 

}; 

#endif // LOGGER_HPP 

和.cpp文件:

其餘

#include <QDebug> 
#include <QThread> 
#include <QCoreApplication> 
#include <QTimer> 
#include "logger.hpp" 
#include "loggee.hpp" 

int main(int argc, char** argv) 
{ 
    QCoreApplication a(argc, argv); 
    QThread t; 
    t.start(); 

    Logger foo; 
    Loggee bar; 
    foo.moveToThread(&t); 

    QObject::connect(&bar, &Loggee::newLogLineNotify, 
        &foo, &Logger::onLogEventHappened, 
        Qt::AutoConnection); 

    qDebug() << "Current thread id: " << QThread::currentThreadId(); 

    bar.someEventHappened(42); 

    QTimer::singleShot(3000, [&]{ bar.someEventHappened(43); }); 

    return a.exec(); 
} 

在本演示中,我創建了一個QThread這種新Logger的插槽的Logger,移動處理到這個新線程的事件循環。然後我使用Qt::AutoConnection連接Loggee的信號與Logger的插槽。這是默認設置,但我明確表示可以更改此設置(即,即使兩個線程都位於同一線程中,也會對該插槽的執行進行排隊的Qt::QueuedConnection)。

然後我導致Loggee發出1個信號。它得到適當的調度,並導致日誌記錄槽在接收器的線程中執行。

¹emit#define emit /*null*/,這樣你就可以忽略它,如果你想

+0

謝謝你的解釋。現在我明白了,我可以把它包裝在一個單獨的記錄器類中。你能告訴我,你是否可以控制線程的優先級?例如,當創建'QThread t'時,你可以使它非常低優先級? – Bremen

+1

有['QThread :: setPriority'](http://doc.qt.io/qt-5/qthread.html#setPriority)。我從來沒有使用它,但它似乎正是你想要的。 – krzaq

2

在Qt中有幾種方法來做通知。

信號和槽

信號和槽可以在線程之間發送,使得連接時,則連接類型設置爲Qt::QueuedConnection or Qt::BlockingQueuedConnection

您可能希望圍繞QStringList創建一個包裝類,以便修改可以觸發其他類偵聽的信號。

QMutex和QWaitCondition

您可以使用Qt的線程同步類QMutexQWaitCondition做經典的手動同步像您以前完成。使用QMutex時,QMutexLocker對於範圍鎖定和釋放QMutex非常有用。

1

如果你只是尋找了Qt等同你已經使用了類:

std::mutex -> QMutex 
std::condition_variable -> QWaitCondition 
std::unique_lock -> QMutexLocker