Функция называется потоко-безопасной (thread-safe), когда она может свободно вызываться из нескольких потоков одновременно. Если две потоко-безопасные функции, вызываемые из разных потоков одновременно, работают с одними и теми же данными, то результат выполнения таких функций всегда предсказуем. Распространив это определение на классы можно сказать, что класс является потоко-безопасным, когда все его методы могут одновременно вызываться из нескольких потоков, без появления непредсказуемых побочных эффектов, даже если они взаимодействуют с оним и тем же объектом.
Среди потоко-безопасных классов в Qt можно назвать: QThread, QMutex, QMutexLocker, QSemaphore, QThreadStorage<T> и QWaitCondition. Кроме того, следующие функции-члены являются потоко-безопасными: QApplication::postEvent(), QApplication::removePostedEvent(), QApplication::removePostedEvents() и QEventLoop::wakeUp().
Большинство невизуальных классов Qt соответствуют менее строгому требованию -- реентерабельности. Класс называется реентерабельным, если он допускает одновременное существование нескольких экземпляров в различных потоках. Однако, одновременный доступ к реентерабельным объектам из нескольких потоков может оказаться далеко не безопасен и потому должен выполняться под защитой мьютексов. Как правило, любой класс C++, который не использует глобальные или иные разделяемые данные, является реентерабельным.
Класс QObject -- реентерабельный, но ни один из его потомков в Qt не является таковым. Как следствие -- мы не можем напрямую обращаться к виджетам вне контекста главного потока приложения. Если, скажем, нужно изменить текст в QLabel из второстепенного потока, то необходимо послать нестандартное событие в главный поток, посредством которого изменить текст надписи.
Операция удаления объекта QObject, с помощью delete, не является реентерабельной. Поэтому, при необходимости удаления объекта QObject из другого потока, нам придется вызвать метод QObject::deleteLater(), который посылает событие "deferred delete".
В контексте любого потока допускается использование механизма сигналов и слотов. При выдаче сигнала, связанный с ним слот исполняется в контексте того же потока, а не в потоке, где был создан объект-приемник. Таким образом сигналы и слоты не могут использоваться для организации взаимодействий между потоками.
Класс QTimer, и классы для работы с сетью QFtp, QHttp, QSocket и QSocketNotifier, целиком зависят от цикла обработки событий, поэтому они не могут использоваться за пределами главного потока. Единственный сетевой класс, который не зависит от цикла обработки событий -- это QSocketDevice, являющийся "оберткой" вокруг платформо-зависимого сетевого API. Некоторые программисты считают, что использование QSocketDevice в синхронном режиме, вне контекста главного потока, дает более простой код, нежели использование QSocket (который работает асинхронно), а благодаря работе вне главного потока -- он не блокирует цикл обработки событий.
Модули SQL и OpenGL так же могут использоваться в многопоточных приложениях, но имеют свои собственные ограничения, которые отличаются от системы к системе. За более подробной информацией обращайтесь по адресу: http://doc.trolltech.com/3.2/sql-driver.html, а так же к статье "Glimpsing the Third Dimension", в ежеквартальнике Qt Quarterly: http://doc.trolltech.com/qq/qq06-glimpsing.html.
Многие из невизуальных классов Qt, включая QImage, QString и другие, используют явные и неявные методы оптимизации, связанные с разделением данных между объектами. Эти классы являются реентерабельными, за исключением конструкторов копирования и операторов присваивания. Когда создается копия объекта, то копируются только указатели на данные. Это может привести к непредсказуемым последствиям, если копии объекта попытаются одновременно, из нескольких потоков, изменить данные. В подобных ситуациях можно прибегнуть к услугам класса QDeepCopy<T>, например:
QString password;
QMutex mutex;
void setPassword(const QString &str)
{
mutex.lock();
password = QDeepCopy<QString>(str);
mutex.unlock();
}
Возможно, Qt 4 будет иметь более широкую поддержку потоков. Так,
среди всего прочего ожидается, что механизм сигналов и слотов будет
расширен до поддержки установления связей через границы потоков, и
позволит отказаться от необходимости создания нестандартных событий для
взаимодействия с главным потоком. Ожидается так же, что невизуальные
классы, подобные QSocket и QTimer, смогут использоваться вне контекста главного
потока, и что необходимость в использовании класса QDeepCopy<T> отпадет.
Пред. | В начало | След. |
Взаимодействие с главным потоком приложения. | На уровень выше | Платформо-зависимые особенности. |