观察 QStringList 的新项目

Watch a QStringList for new items

我正在 QT 框架中开发数据记录器。我打算将日志字符串保存到文件中,并在单独的观察线程中打印到控制台。在那个单独的线程中,我需要观察我的 QStringList 是否添加了新项目。如果有新项目,我将它们从队列中取出并记录下来。我想知道 Qt 框架中使用的机制是什么。在 STD 库中,我使用 condition_variable 来完成这样的任务:

/*!
 * \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

在 Qt 中有几种方法可以进行通知。

信号与槽

信号和槽可以在线程间传递,连接时将连接类型设置为Qt::QueuedConnection or Qt::BlockingQueuedConnection

您可能想要围绕 QStringList 创建一个包装器 class,以便修改可以触发其他 classes 侦听的信号。

QMutex 和 QWaitCondition

您可以使用 Qt 线程同步 classes QMutex and QWaitCondition to do the classic manual synchronisation like you have done previously. When using QMutex, the QMutexLocker 对于 QMutex 的作用域锁定和释放很有用。

下面是执行此信号和槽的示例。您可能想做自己的基准测试来测试这是否符合您的需要。另请注意,虽然信号和槽保证线程安全,但它们不保证消息将按照发送时的相同顺序出现。话虽这么说,我已经使用这种机制多年了,但它还没有发生在我身上。

首先,创建一对 类:

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 "logger.hpp"

void Logger::onLogEventHappened(QString const& str)
{
    qDebug() << QThread::currentThreadId() << str;
}

其余

#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::AutoConnectionLoggee 的信号与 Logger 的插槽连接起来。这是默认设置,但我明确声明它是为了表明您可以更改它(即 Qt::QueuedConnection,即使两个线程都在同一个线程中,它也会对插槽的执行进行排队)。

然后我让 Loggee 发出一个信号。它得到适当的调度并导致日志槽在接收方的线程中执行。

¹ emit#define emit /*null*/,所以你可以省略它

如果您只是在寻找与您一直在使用的 类 等效的 Qt:

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