Is qDebug() thread-safe?

2019-02-05 17:21发布

问题:

Is qDebug() thread-safe? By thread-safe I don't just mean not-crashing, but also if I call qDebug() from different threads, is it possible for the output to become mixed-up? I tested it with this code, and it doesn't appear to be so, however, I couldn't find anywhere in the documentation where they talk about this.

This is my test code:

#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();
}

There were no 'a' and 'b' mixed in the same line anywhere, but I'm still not sure if it's 100% thread safe...

回答1:

Following are my answer and comments:

  1. If the documentation of qDebug() does not mention whether it is thread-safe or not, we should assume it is not. The answer is likely platform-dependent: how qDebug() is implemented at the system level (Linux, Windows, ...).

  2. Instead of the broader question of thread-safety, I think you were asking a more specific question like this: "Will the use of qDebug() in a multi-threaded application lead to interleaved output lines?" The answer is "Yes, occasionally." as demonstrated by the results produced by @dmcontador above. And the probability increases when the strings to be printed out are getting longer, as explained by @quetzalcoatl above.

  3. The answer does not depend on whether you use qDebug("...") or qDebug() << "...", as both will finally call the system-level implementation code.

  4. It is not easy for me to produce interleaved output lines using your original example code. So I have created a new example as shown below:

    #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;
    }
    

The probability increases when you increase MAX_LEN.

  1. A follow-up question would be: "How to use qDebug() to produce non-interleaved output lines?" One solution would be to use QMutex on each and every qDebug() line. Note that I have not tried this solution which is not practical.


回答2:

The docs say If a function is not marked as thread-safe or reentrant, it should not be used from different threads. In case of qDebug() it doesn't say it's thread-safe or reentrant, so it's probably not safe to use from different threads.



回答3:

I'm afraid that it is not thread-safe. Also, I tried your code and had mixed output.

aaaaaaaaaaaabbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbb

I had the same luck with qDebug() << "..."

Tested in Qt5.2.1 with mingw48_32 compiler.



回答4:

I've found such thing: http://www.qtcentre.org/threads/28879-redirecting-qDebug-to-file-threading-question

Quoting:

To answer the question if qdebug is threadsafe: QDebug uses a QTextstream. A QTextStream is not threadsafe. The documentation is not clear about this, but if you look at the source code of qdebug or qtextstream you see there's no mutex locking at all in the code.



回答5:

Practically qDebug( ..text.. ) is thread-safe (at least if compiled with gcc).

If you look in the qt (4) source file qglobal.cpp, qDebug calls qt_message_output which calls fprintf(stderr, ...), which is thread-safe in glibc

(qDebug() << .. is another story )



回答6:

Both

qDebug("xx")

as well as

qDebug() << "xx"

qInfo, qWarning, qCritical, and the categorized versions like qCDebug, qCInfo, qCWarning, qCritical are safe to be used concurrently from different threads.

However, you have to make sure that the log sink can also handle large data atomically. This is were the confusion comes from, because stderr apparently breaks line that are too long. You can easily verify this by just replacing qDebug() by fprintf(stderr) in the example: It shows exactly the same behavior for me.

You can try other logging sinks, like journald. Anyhow, they might impose restrictions on the maximum length, too. In general I'd suggest to keep the maximum length of a log message reasonable.