Главная · Все классы · Все функции · Обзоры

[Предыдущая: Синхронизация потоков] [Поддержка потоков в Qt] [Следующая: Потоки и объекты QObjects]

Реентерабельность и потокобезопасность

Везде в документации термины реентерабельность и потокобезопасность используются для обозначения классов и функций для указания того, как они могут быть использованы в многопоточных приложениях:

Таким образом, потокобезопасная функция всегда реентерабельна, но реентерабельная функция не всегда потокобезопасна.

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

Классы C++ часто реентерабельны просто потому, что они имеют доступ только к данным своих членов. Любой поток может вызвать функцию-член экземпляра реентерабельного класса, в то время как ни один другой поток не может вызвать функцию-член того же самого экземпляра класса в тоже самое время. Например, нижеуказанный класс Counter является реентерабельным:

 class Counter
 {
 public:
     Counter() { n = 0; }

     void increment() { ++n; }
     void decrement() { --n; }
     int value() const { return n; }

 private:
     int n;
 };

Данный класс не является потокобезопасным, поскольку если несколько потоков попытаются изменить член данных n, результат будет не определен. Это так, потому что операторы ++ и -- не всегда атомарны. В действительности, они обычно расширяются до трех машинных инструкций:

  1. Загрузка значения переменной в регистр.
  2. Увеличение или уменьшение значения регистра.
  3. Сохранение значения регистра обратно в основную память.

Потоки A и B одновременно могут загрузить старое значение переменной, увеличить ее значение в регистре и сохранить значение переменной в памяти, но переменная будет увеличена только однажды!

Поток A должен выполнить шаги 1, 2, 3 без прерывания (атомарно) прежде, чем поток B сможет выполнить те же шаги; или наоборот. Самый легкий способ создания потокобезопасного класса состоит в том, чтобы защитить весь доступ к членам данных с помощью QMutex:

 class Counter
 {
 public:
     Counter() { n = 0; }

     void increment() { QMutexLocker locker(&mutex); ++n; }
     void decrement() { QMutexLocker locker(&mutex); --n; }
     int value() const { QMutexLocker locker(&mutex); return n; }

 private:
     mutable QMutex mutex;
     int n;
 };

Класс QMutexLocker автоматически запирает мьютекс в своем конструкторе и отпирает его в деструкторе, вызываемом при завершении функции. Запирание мьютекса гарантирует, что обращения из разных потоков будут упорядочены. Член данных mutex объявлен как mutable, потому что позволяет запереть и отпереть мьютекс в функции value(), которая является константной.

Большинство классов Qt реентерабельны, но не потокобезопасны, поскольку такая реализация потребовала бы дополнительных издержек на многократные блокировки и разблокировки QMutex. Например, QString реентерабелен, но не потокобезопасен. Вы можете смело обращаться к различным экземплярам класса QString из нескольких потоков одновременно, но вы не можете спокойно получить доступ к одному и тому же экземпляру QString из нескольких потоков одновременно (если вы самостоятельно не обеспечиваете защиту от доступа с помощью QMutex).

Некоторые классы и функции Qt потокобезопасны. Это, главным образом, связанные с потоками классы (например, QMutex) и фундаментальные функции (например, QCoreApplication::postEvent()).

Замечание: Классы Qt документируются как потокобезопасные, только если они предназначены для работы в многопоточных приложениях.

Замечание: Терминология в многопоточной области еще не полностью стандартизована. POSIX использует несколько отличающиеся определения реентерабельности и потокобезопасности для своих API C. При использовании других объектно-ориентированных библиотек классов C++ совместно с Qt убедитесь, что их определения понятны.

[Предыдущая: Синхронизация потоков] [Поддержка потоков в Qt] [Следующая: Потоки и объекты QObjects]


Авторские права © 2010 Nokia Corporation и/или её дочерние компании Торговые марки
Qt 4.6.4