25

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%线程安全......

4

7 回答 7

17

以下是我的回答和评论:

  1. 如果 qDebug() 的文档没有提到它是否是线程安全的,我们应该假设它不是。答案可能取决于平台:如何在系统级别(Linux、Windows、...)实现 qDebug()。

  2. 我认为您不是在问更广泛的线程安全问题,而是在问这样一个更具体的问题:“在多线程应用程序中使用 qDebug() 会导致交错的输出行吗?” 答案是“是的,偶尔”。正如上面@dmcontador 产生的结果所证明的那样。正如上面@quetzalcoatl 所解释的,当要打印的字符串变得更长时,概率会增加。

  3. 答案并不取决于您使用的是 qDebug("...") 还是 qDebug() << "...",因为两者最终都会调用系统级实现代码。

  4. 使用您的原始示例代码生成交错输出行对我来说并不容易。所以我创建了一个新示例,如下所示:

    #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 时,概率会增加。

  1. 一个后续问题是:“如何使用 qDebug() 生成非交错输出行?” 一种解决方案是在每个 qDebug() 行上使用 QMutex。请注意,我没有尝试过这种不实用的解决方案。
于 2015-04-30T00:05:03.450 回答
15

文档说如果函数未标记为线程安全或可重入,则不应从不同的线程中使用它。如果qDebug()它说:Note: This function is thread-safe

这个答案已更新......文档没有说明该函数之前是线程安全的。

于 2014-03-20T12:00:34.950 回答
8

恐怕它不是线程安全的。另外,我尝试了您的代码并且输出混合。

aaaaaaaaaaaabbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbb

我有同样的运气qDebug() << "..."

使用 mingw48_32 编译器在 Qt5.2.1 中测试。

于 2014-05-07T12:23:59.790 回答
5

实际上,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 函数,该函数再次调用相同的处理程序)。

于 2019-02-05T13:21:40.820 回答
2

我发现了这样的事情:http ://www.qtcentre.org/threads/28879-redirecting-qDebug-to-file-threading-question

报价:

要回答 qdebug 是否是线程安全的问题:QDebug 使用 QTextstream。QTextStream 不是线程安全的。文档对此并不清楚,但是如果您查看 qdebug 或 qtextstream 的源代码,您会发现代码中根本没有互斥锁。

于 2014-03-20T09:27:55.230 回答
2

实际上qDebug( ..text.. )是线程安全的(至少如果用 gcc 编译的话)。

如果你查看 qt(4) 源文件qglobal.cppqDebug调用qt_message_outputwhich calls fprintf(stderr, ...),这在 glibc 中是线程安全的

qDebug() << .. 是另一个故事)

于 2014-03-20T15:12:56.323 回答
1

两个都

qDebug("xx")

qDebug() << "xx"

qInfo、qWarning、qCritical 以及 qCDebug、qCInfo、qCWarning、qCritical 等分类版本可以安全地从不同线程同时使用。

但是,您必须确保日志接收器也可以原子地处理大数据。这是混乱的来源,因为 stderr 显然打破了太长的行。您可以通过在示例中将 qDebug() 替换为 fprintf(stderr) 轻松验证这一点:它对我显示完全相同的行为。

您可以尝试其他日志接收器,例如 journald。无论如何,他们也可能对最大长度施加限制。一般来说,我建议保持日志消息的最大长度合理。

于 2017-10-12T11:59:19.887 回答