qDebug()
线程安全吗?我所说的线程安全不仅意味着不崩溃,而且如果我从不同的线程调用qDebug()
,输出是否有可能混淆?我用这段代码测试了它,似乎并非如此,但是,我在文档中找不到他们谈论这一点的任何地方。
这是我的测试代码:
#include <QtConcurrent>
#include <QApplication>
void print_a() {
for (int ii = 0; ii < 10000; ii++) {
qDebug("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
}
}
void print_b()
{
for (int ii = 0; ii < 10000; ii++) {
qDebug("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QtConcurrent::run(print_a);
QtConcurrent::run(print_b);
return a.exec();
}
任何地方都没有在同一行中混合"a"和"b">,但我仍然不确定它是否 100% 线程安全......
我的回答和评论:
如果 qDebug(( 的文档没有提到它是否是线程安全的,我们应该假设它不是。答案可能取决于平台:qDebug((如何在系统级别(Linux,Windows等(实现。
我认为您问的不是更广泛的线程安全问题,而是像这样更具体的问题:"在多线程应用程序中使用 qDebug(( 会导致交错输出行吗?答案是"是的,偶尔",正如上述@dmcontador得出的结果所证明的那样。当要打印的字符串变长时,概率会增加,如上面的@quetzalcoatl所述。
答案并不取决于你是使用 qDebug("..."( 还是 qDebug(( <<"...",因为两者都最终会调用系统级实现代码。
使用您的原始示例代码生成交错输出行对我来说并不容易。所以我创建了一个新示例,如下所示:
#include <QCoreApplication> #include <QtConcurrent> #define MAX_ITERS 10 #define MAX_LEN 10000 void print_a() { QString a(MAX_LEN, 'a'); for(int i = 0; i < MAX_ITERS; ++i) { qDebug().noquote() << a; } } void print_b() { QString b(MAX_LEN, 'b'); for(int i = 0; i < MAX_ITERS; ++i) { qDebug().noquote() << b; } } int main(int argc, char * argv[]) { QCoreApplication a(argc, argv); QtConcurrent::run(print_a); QtConcurrent::run(print_b); return 0; }
当您增加MAX_LEN时,概率会增加。
- 后续问题是:"如何使用 qDebug(( 生成非交错输出行?一种解决方案是在每一行qDebug((上使用QMutex。请注意,我还没有尝试过这个不切实际的解决方案。
文档说,如果一个函数没有被标记为线程安全或可重入,则不应从不同的线程使用它。如果是qDebug()
,它说:Note: This function is thread-safe
。
(此答案已更新...文档之前没有说明该函数是线程安全的。
怕它不是线程安全的。另外,我尝试了您的代码并有混合输出。
aaaaaaaaaaaabbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbb
我和qDebug() << "..."
运气一样
在Qt5.2.1中使用mingw48_32编译器进行了测试。
事实上,与QtDebug相关的函数(如qDebug((,qWarning((等(所做的是调用可以通过调用qInstallMessageHandler((来设置的消息处理程序。所以这取决于你 - 这个消息处理程序是否是线程安全的。有一个默认实现只是将消息打印到 stderr,它不会阻止来自不同线程的混合输出,因此,如果您希望始终为来自不同线程的调试消息、警告和错误提供非混合的逐行输出,您应该安装自己的处理程序,具有某种锁定(例如 QMutex(,如下所示:
QMutex messageMutex;
void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
QMutexLocker locker(&messageMutex);
[print msg and context somewhere]
}
int main(int argc, char **argv)
{
qInstallMessageHandler(myMessageHandler);
QApplication app(argc, argv);
[...]
}
注意:正如 Kuba Ober 正确指出的那样,这应该谨慎使用(不过,就像一般的任何锁定一样(。例如,如果使用相同的 I/O 库输出调试消息时从 I/O 库内部调用 QtDebug 函数,则可能会出现死锁(例如,当 QtDebug 消息处理程序在对非递归互斥锁保持锁的同时调用 I/O,则底层 I/O 机制调用某个回调函数,然后此回调函数调用 QtDebug 函数, 再次调用同一处理程序(。
我发现了这样的事情:http://www.qtcentre.org/threads/28879-redirecting-qDebug-to-file-threading-question
引用:
要回答 qdebug 是否是线程安全的问题: QDebug 使用 QTextstream。QTextStream 不是线程安全的。 文档对此并不清楚,但是如果您查看qdebug或qtextstream的源代码,您会发现代码中根本没有互斥锁。
实际上qDebug( ..text.. )
是线程安全的(至少如果使用 gcc 编译(。
如果你查看 qt (4( 源文件qglobal.cpp
,qDebug
调用qt_message_output
调用 fprintf(stderr, ...)
,这在 glibc 中是线程安全的
(qDebug() << ..
是另一回事(
两者
qDebug("xx")
以及
qDebug() << "xx"
qInfo,qWarning,qCritical和分类版本如qCDebug,qCInfo,qCWarning,qCritical可以安全地从不同的线程同时使用。
但是,必须确保日志接收器也可以原子方式处理大型数据。这就是混乱的来源,因为stderr显然打破了太长的行。您可以通过在示例中将 qDebug(( 替换为 fprintf(stderr( 来轻松验证这一点:它为我显示了完全相同的行为。
可以尝试其他日志记录接收器,例如日志。无论如何,他们也可能对最大长度施加限制。一般来说,我建议保持日志消息的最大长度合理。