Domanda

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...

È stato utile?

Soluzione

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.

Altri suggerimenti

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 says: Note: This function is thread-safe.

(this answer was updated... the docs didn't state that the function was thread-safe before.)

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.

In fact, what QtDebug-related functions (such as qDebug(), qWarning() and so on) do, is call message handler that can be set by calling qInstallMessageHandler(). So it's up to you - will this message handler be thread-safe or not. There is a default implementation which just prints messages to stderr, it doesn't prevent mixed output from different threads, so, if you want to have always non-mixed line-by-line output for your debug messages, warnings and errors from different threads, you should install your own handler with some sort of locking (such as QMutex) like this:

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);

    [...]
}

NOTE: as Kuba Ober correctly noted, this should be used with caution (like any locking in general, though). For example, you can get a deadlock if QtDebug functions are called from I/O library internals while using same I/O library to output debug messages (this is possible, for example, when QtDebug message handler calls I/O while holding a lock on non-recursive mutex, then underlying I/O machinery calls some callback function, and then this callback function calls QtDebug function, which calls the same handler again).

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.

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 )

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top