Подтвердить что ты не робот

Qt5: Как использовать qDebug() для входа в файл, многопоточного приложения

Я начал использовать Qt5 несколько дней назад. Мне нужен регистратор для моего приложения, и я решил использовать qDebug, но, похоже, он должен быть "перенаправлен", чтобы иметь журналы в файле.

Я использовал qInstallMessageHandler чтобы сделать это, и я написал собственный обработчик, как qInstallMessageHandler ниже (вдохновленный другими людьми здесь).

Кажется, это работает, но поскольку я не гуру, я должен спросить: можно ли использовать это в многопоточном приложении или нет?

Кроме того, если это нормально/безопасно для использования в многопоточном приложении, можно ли это как-то улучшить?

void MessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg)
{
    mutex.lock();

    QDateTime dateTime(QDateTime::currentDateTime());

    QString timeStr(dateTime.toString("dd-MM-yyyy HH:mm:ss:zzz"));
    QString contextString(QString("(%1, %2)").arg(context.file).arg(context.line));

    QFile outFile("file.log");
    outFile.open(QIODevice::WriteOnly | QIODevice::Append);

    QTextStream stream(&outFile);
    stream << timeStr << " " << contextString << ": " << msg << endl;

    mutex.unlock();
}
4b9b3361

Ответ 1

Ваш подход выглядит простым и чистым. Я бы сохранил это так.

Есть одна вещь, которую вы можете улучшить: откройте файл только один раз при запуске приложения и закройте его, когда вы закрываете приложение. Открытие файла - дорогостоящая операция.

Вы можете писать в один и тот же открытый файл из нескольких потоков, поскольку ваш мьютекс гарантирует, что одновременно будет записываться только один поток.

Ответ 2

Вы не можете найти нигде в документации Qt, что qDebug является поточно-безопасным. Поэтому небезопасно вызывать его одновременно из нескольких потоков, и действительно, вы столкнетесь с смешанным выходом, если вы не используете механизм блокировки.

Ваш подход к блокировке будет лучше, если вы используете QMutexLocker, как это настоятельно рекомендуется в документации Qt:

void MessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg)
{
    QMutexLocker locker(&mutex);
    ...
}

Второй подход заключается в том, чтобы предоставить рабочий класс, который имеет слоты для записи журнала. Затем вы можете получить его экземпляр в новом потоке и вызвать его слоты в обработчике сообщений, используя QMetaObject::invokeMethod с типом соединения Qt::QueuedConnection. Таким образом, каждый вызов из каждого потока будет помещен в очередь и обрабатывается в рабочем потоке, и он, вероятно, будет иметь лучшую производительность, поскольку все задания выполняются в отдельном потоке.

Ответ 3

Я не видел ответа на ваши опасения по поводу вялого, я уверен, что к этому времени вы что-то поняли. Вероятно, вы могли бы повысить производительность за счет сокращения количества кода в критической области. Операции с файлами медленны, поэтому старайтесь сократить время, затрачиваемое на их выполнение. Это может включать в себя сохранение открытого файла, периодическую буферизацию журнала и запись, построение записи из заблокированной области и

void MessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg)
{

    QDateTime dateTime(QDateTime::currentDateTime());

    QString timeStr(dateTime.toString("dd-MM-yyyy HH:mm:ss:zzz"));
    QString contextString(QString("(%1, %2)").arg(context.file).arg(context.line));
    QString entryString("%1 %2: %3");
    entryString = entryString.arg(timeStr).arg(contextString).arg(msg);

    QFile outFile("file.log");

    mutex.lock();
    outFile.open(QIODevice::WriteOnly | QIODevice::Append);  
    QTextStream stream(&outFile);  
    stream << entryString << endl;    
    mutex.unlock();
}