qDebug()线程安全吗?

sas*_*alm 25 c++ qt multithreading qdebug

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();
}
Run Code Online (Sandbox Code Playgroud)

任何地方没有'a'和'b'混合在同一行,但我仍然不确定它是否100%线程安全......

jon*_*nzh 17

以下是我的回答和评论:

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

  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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

增加MAX_LEN时概率会增加.

  1. 后续问题是:"如何使用qDebug()生成非交错输出线?" 一种解决方案是在每个qDebug()行上使用QMutex.请注意,我没有尝试过这种不实用的解决方案.

  • qDebug 和 QDebug 本身是线程安全的。您所看到的“交错输出”源于以下事实:stderr(在大多数配置中默认情况下 Qt 会记录到该输出)没有缓冲。如果您的队列很长,我建议使用不同的日志记录接收器(例如 Linux 上的 syslogd)。您可以通过用 fprintf(stderr, ...) 调用替换 qDebug() 来轻松检查这一点。 (2认同)

thu*_*uga 14

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

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


dmc*_*dor 8

我担心它不是线程安全的.此外,我尝试了你的代码并混合输出.

aaaaaaaaaaaabbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbb
Run Code Online (Sandbox Code Playgroud)

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

使用mingw48_32编译器在Qt5.2.1中进行了测试.


Ole*_*etz 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);

    [...]
}
Run Code Online (Sandbox Code Playgroud)

注意:正如Kuba Ober正确指出的那样,应该谨慎使用(不过就像一般的锁定一样)。例如,如果从 I/O 库内部调用 QtDebug 函数,同时使用相同的 I/O 库输出调试消息,则可能会出现死锁(例如,当 QtDebug 消息处理程序在持有锁的情况下调用 I/O 时,这是可能的)在非递归互斥体上,然后底层 I/O 机器调用一些回调函数,然后该回调函数调用 QtDebug 函数,该函数再次调用相同的处理程序)。