Подтвердить что ты не робот

Является ли 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();
}

В любом месте нет 'a' и 'b' смешались в одной строке, но я все еще не уверен, что он на 100% безопасен в потоке...

4b9b3361

Ответ 1

Ниже приведены мои ответы и комментарии:

  • Если в документации qDebug() не упоминается, является ли она потокобезопасной или нет, мы должны предположить, что это не так. Ответ, вероятно, зависит от платформы: как qDebug() реализуется на системном уровне (Linux, Windows,...).

  • Вместо более широкого вопроса о безопасности потоков, я думаю, вы задавали более конкретный вопрос: "Будет ли использование qDebug() в многопоточном приложении привести к чередующимся выходным линиям?" Ответ: "Да, время от времени". о чем свидетельствуют результаты, приведенные выше. И вероятность возрастает, когда строки, которые нужно распечатать, становятся длиннее, как объяснялось выше @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.

  1. Следующим вопросом будет: "Как использовать qDebug() для создания строк без чередования?" Одним из решений было бы использовать QMutex для каждой строки qDebug(). Обратите внимание, что я не пробовал это решение, которое не практично.

Ответ 2

Документы говорят Если функция не помечена как потокобезопасная или реентерабельная, ее нельзя использовать из разных потоков. В случае qDebug() он не говорит, что он потокобезопасен или реентерабелен, поэтому он, вероятно, небезопасен для использования из разных потоков.

Ответ 3

Я боюсь, что это не потокобезопасно. Кроме того, я пробовал ваш код и имел смешанный выход.

aaaaaaaaaaaabbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbb

Мне повезло с qDebug() << "..."

Протестировано в Qt5.2.1 с помощью компилятора mingw48_32.

Ответ 4

Я нашел такую ​​вещь: http://www.qtcentre.org/threads/28879-redirecting-qDebug-to-file-threading-question

Цитирование:

Чтобы ответить на вопрос, является ли qdebug потокобезопасным: QDebug использует QTextstream. QTextStream не является потокобезопасным. Документация не совсем понятна, но если вы посмотрите на исходный код qdebug или qtextstream, вы увидите, что в коде вообще нет блокировки мьютекса.

Ответ 5

Практически qDebug( ..text.. ) является потокобезопасным (по крайней мере, если скомпилирован с gcc).

Если вы посмотрите в исходном файле qt (4) qglobal.cpp, qDebug вызывает qt_message_output, который вызывает fprintf(stderr, ...), который является потокобезопасным в glibc

(qDebug() << .. - это еще одна история)

Ответ 6

Фактически, то, что делают функции, связанные с 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);

    [...]
}

ПРИМЕЧАНИЕ: как правильно заметил Куба Обер, это следует использовать с осторожностью (хотя, как и в случае любой другой блокировки в целом). Например, вы можете получить взаимоблокировку, если функции QtDebug вызываются из внутренних компонентов библиотеки ввода-вывода при использовании той же библиотеки ввода-вывода для вывода сообщений отладки (это возможно, например, когда обработчик сообщений QtDebug вызывает ввод-вывод при удержании блокировки на нерекурсивном мьютексе базовый механизм ввода-вывода вызывает некоторую функцию обратного вызова, а затем эта функция обратного вызова вызывает функцию QtDebug, которая снова вызывает тот же обработчик).

Ответ 7

И

qDebug("xx")

а также

qDebug() << "xx"

qInfo, qWarning, qCritical и категоризированные версии, такие как qCDebug, qCInfo, qCWarning, qCritical безопасны для одновременного использования из разных потоков.

Тем не менее, вы должны убедиться, что приемник журналов также может обрабатывать большие данные атомарно. Это была путаница, потому что stderr, по-видимому, ломает слишком длинную линию. Вы можете легко проверить это, просто заменив qDebug() на fprintf (stderr) в примере: для меня это точно такое же поведение.

Вы можете попробовать другие приемники, например journald. Во всяком случае, они могут также налагать ограничения на максимальную длину. В общем, я бы предложил сохранить максимально допустимую длину сообщения журнала.