[Предыдущая: Синхронизация потоков] [Поддержка потоков в 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, результат будет не определен. Это так, потому что операторы ++ и -- не всегда атомарны. В действительности, они обычно расширяются до трех машинных инструкций:
- Загрузка значения переменной в регистр.
- Увеличение или уменьшение значения регистра.
- Сохранение значения регистра обратно в основную память.
Потоки 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 |
|