Multithreading Qt5:如何使用qDebug()登录文件、多线程应用程序

Multithreading Qt5:如何使用qDebug()登录文件、多线程应用程序,multithreading,qt,logging,thread-safety,Multithreading,Qt,Logging,Thread Safety,我几天前开始使用Qt5。我的应用程序需要一个记录器,我决定使用qDebug,但似乎它必须“重定向”才能将日志保存在文件中 我使用了qInstallMessageHandler来实现这一点,我编写了自己的处理程序,如下所示(灵感来自这里的其他人) 这似乎有效,但由于我不是大师,我不得不问: 在多线程应用程序中使用它可以吗 此外,如果在多线程应用程序中使用它是正常/安全的,那么它是否可以改进 void MessageHandler(QtMsgType type, const QMessageLogC

我几天前开始使用Qt5。我的应用程序需要一个记录器,我决定使用
qDebug
,但似乎它必须“重定向”才能将日志保存在文件中

我使用了
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();
}
void MessageHandler(QtMsgType类型,常量QMessageLogContext&context,常量QString&msg)
{
mutex.lock();
QDateTime日期时间(QDateTime::currentDateTime());
QString timeStr(dateTime.toString(“dd-MM-yyy-HH:MM:ss:zzz”);
QString contextString(QString((%1,%2)”).arg(context.file.arg(context.line));
QFile输出文件(“file.log”);
open(QIODevice::WriteOnly | QIODevice::Append);
QTextStream流(&outFile);

stream在Qt文档中找不到
qDebug
是线程安全的。因此,从多个线程同时调用它是不安全的,如果不使用锁定机制,确实会遇到混合输出

如果您使用
QMutexLocker
,您的锁定方法会更好,因为Qt文档强烈建议:

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

第二种方法是提供一个工作类,该工作类具有用于写入日志的插槽。然后,您可以在新线程中拥有该工作类的实例,并使用
QMetaObject::invokeMethod
Qt::QueuedConnection
连接类型在消息处理程序中调用它的插槽。这样,来自每个线程的每个调用都将排队并在e worker线程,它可能会有更好的性能,因为所有的工作都在一个单独的线程中完成。

您的方法看起来简单明了。我会保持这样

有一点可以改进:在应用程序启动时只打开一次文件,在关闭应用程序时关闭它。打开文件是一项昂贵的操作


您可以从多个线程写入同一个打开的文件,因为您的互斥锁确保只有一个线程同时写入。

我看不到您对缓慢的担忧有任何回应,我相信此时您已经找到了一些办法。您可能可以通过减少关键区域内的代码量来提高性能。文件操作ns速度很慢,因此请尽量减少执行这些操作所花费的时间。这可以包括保持文件打开、缓冲日志和定期写入、从锁定区域构造条目以及

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();
}
void MessageHandler(QtMsgType类型,常量QMessageLogContext&context,常量QString&msg)
{
QDateTime日期时间(QDateTime::currentDateTime());
QString timeStr(dateTime.toString(“dd-MM-yyy-HH:MM:ss:zzz”);
QString contextString(QString((%1,%2)”).arg(context.file.arg(context.line));
QString入口字符串(“%1%2:%3”);
entryString=entryString.arg(timeStr).arg(contextString).arg(msg);
QFile输出文件(“file.log”);
mutex.lock();
open(QIODevice::WriteOnly | QIODevice::Append);
QTextStream流(&outFile);

但是,如果“QMutexLocker locker(&mutex);”的作用与mutex.lock()后跟mutex.unlock()的作用相同,唯一的区别是您不必担心该函数/方法是否有多个退出/返回点。由于locker是一个自动变量,因此在退出时将自动解锁mutex。顺便问一下,关于这一点,SG.BTW:如何“就速度而言”…?互斥锁/解锁()还是QMutexLocker机制,哪一种执行速度更快?我不知道它们在速度方面是否不同。但第二种方法是使用工作线程并在那里执行操作。请参见编辑后的答案。我对您的“第二种方法”感兴趣,但因为我只是Qt(和C++)的初学者我真的无法实现代码/编码…请您详细说明一下?我真的很想尝试这种方法!哪种类型有您的变量
互斥体
?静态QMutex互斥体;//全局变量您正在为哪个操作系统开发?这将是一个服务器应用程序吗?您希望有多少不同的线程?是的,确实是一个服务器应用程序大约有30个线程(最多)。-可以使用4个线程,但是如果使用30个线程,它会变得非常缓慢。Simon,感谢您花时间和您的建议!我只会在启动时打开文件一次,但是(在不久的将来)我计划将文件名从“file.log”更改为类似“dd-mm-yyy.log”的内容“…我相信你知道我的意思:)因此,在应用程序启动时打开日志文件是不可能的…对此问题有任何想法/解决方法吗?提前谢谢!@groenhen你是否希望应用程序运行一天以上?是的,它将不间断地运行(至少我希望如此:)…)@groenhen一步一个脚印,伙计。如果你已经启动并运行了所有程序,你可以在以后添加一个函数来关闭文件,将其移动到存档并打开一个新的。因为这个函数和日志编写器一样锁定同一个互斥锁,所以这样做没有更大的麻烦。@nat endl可能会刷新到磁盘上。但是你可以明确地刷新呃,每一根日志线都是超级安全的。