Потоки и объекты QObjectQThread унаследован от QObject. Он испускает сигналы, сообщающие о том, что поток начал или закончил работу, а также предоставляет несколько слотов. Более интересным является то, что объекты QObject могут использоваться в многопоточном приложении, испускать сигналы, приходящие в слоты, находящиеся в других потоках, и посылать события объектам, "живущим" в других потоках. Это возможно, потому что каждый поток имеет собственный цикл обработки событий. Реентерабельность QObjectQObject реентерабелен. Большинство из его неграфических (non-GUI) подклассов, таких как QTimer, QTcpSocket, QUdpSocket, QFtp и QProcess, также реентерабельны, что делает возможным их использование из нескольких потоков одновременно. Заметим, что эти классы спроектированы для создания и использования в одном потоке; создание объекта в одном потоке и вызов его функций из другого может и не сработать. Вы должны знать о трёх ограничениях:
Несмотря на то, что QObject реентерабелен, классы ГПИ, особенно QWidget и все его подклассы, таковыми не являются. Они могут использоваться только из главного потока. Как было сказано ранее, QCoreApplication::exec() также должен вызываться из главного потока. На практике невозможно использовать классы ГПИ в других потоках, кроме главного, но выполнение продолжительных действий можно легко поместить в отдельный поток и отображать на экране результаты выполнения средствами главного потока по окончании обработки во вспомогательном потоке. Такой подход используется в примерах "Mandelbrot" и "Blocking Fortune Client". Цикл обработки событий потокаКаждый поток может иметь собственный цикл обработки событий. Главный поток начинает цикл обработки событий, используя QCoreApplication::exec(); другие потоки могут начать свои циклы обработки событий, используя QThread::exec(). Подобно QCoreApplication, QThread предоставляет функцию exit(int) и слот quit(). Цикл обработки событий потока делает возможным использование потоком некоторых неграфических классов Qt, которые требуют наличия цикла обработки событий (такие как QTimer, QTcpSocket и QProcess). Это также даёт возможность соединить сигналы из любых потоков со слотами в определённом потоке. В разделе Соединение сигналов и слотов между потоками это описано подробнее. Экземпляр QObject считается живущим в потоке, в котором он был создан. События этому объекту пересылаются циклом обработки событий потока. Поток, в котором живет QObject, может быть получен с помощью QObject::thread(). Обратите внимание на то, что для объектов QObject, которые созданы до QApplication, QObject::thread() возвратит ноль. Это означает, что только главный поток может обрабатывать события для этих объектов; для объектов без потока обработка событий не происходит. Используйте функцию QObject::moveToThread() для изменения принадлежности к потоку объекта и его детей (объект не может быть перемещён, если у него есть родитель). Вызов delete для объекта QObject из другого потока, нежели тот, который является собственником объекта (или вообще обращение к объекту) небезопасно, если вы не гарантируете того, что объект не обрабатывает события в этот момент. Вместо этого используйте QObject::deleteLater(), и будет отправлено событие DeferredDelete, которое, в конце концов, будет обработано циклом обработки событий данного объекта. По умолчанию, поток, которому принадлежит QObject, - это поток, который создал QObject, и в дальнейшем не была вызвана функция QObject::moveToThread(). Если никакой цикл обработки событий не запущен, то события не будут доставлены объекту. Например, если вы создаёте объект QTimer в потоке, который никогда не вызывает exec(), то QTimer никогда не испустит сигнал timeout(). Вызов deleteLater() также не сработает. (Эти ограничения относятся также и к главному потоку.) Вы можете вручную послать событие любому объекту в любом потоке используя потокобезопасную функцию QCoreApplication::postEvent(). События будут автоматически посланы циклу обработки событий потока, в котором объект был создан. Фильтры событий поддерживаются во всех потоках, с условием, что контролируемый объект должен располагаться в том же потоке, что и контролирующий объект. Аналогично, QCoreApplication::sendEvent() (в отличие от postEvent()) может использоваться только для отправки событий объектам, живущим в том же потоке, что и посылающая события функции. Организация доступа к подклассам QObject из других потоковQObject и все его подклассы не потокобезопасны. Это влияет на всю систему доставки событий. Важно помнить, что цикл обработки событий может доставлять события вашему подклассу QObject в то время, как вы обращаетесь к объекту из другого потока. Если вы вызываете функцию подкласса QObject, не живущего в текущем потоке, и объект может получать события, то вы должны защитить все обращения к данным вашего подкласса QObject с помощью мьютекса; в противном случае вы можете получить крах программы или другое неожиданное поведение. Подобно другим объектам, QThread "живет" в потоке, в котором он был создан - не в потоке, который создан при вызове QThread::run(). Вообще, опасно иметь слоты в вашем подклассе QThread, если вы не защищаете переменные-члены с помощью мьютекса. С другой стороны, вы можете спокойно испускать сигналы вашей реализацией QThread::run(), потому что испускание сигналов потокобезопасно. Соединение сигналов и слотов между потокамиQt поддерживает следующие типы соединений сигнал-слот:
Это можно изменить, передав дополнительный аргумент в connect(). Помните, что использование прямых соединений, когда отправитель и получатель "живут" в разных потоках, опасно в случае, если цикл обработки событий выполняется в потоке, где "живет" приемник, по той же самой причине, по которой небезопасен вызов функций объекта, принадлежащего другому потоку. QObject::connect() сама по себе потокобезопасна. В примере "Mandelbrot" используется соединение через очередь для установки связи между рабочим и основным потоками. Для того, чтобы избежать "заморозки" цикла обработки событий основного потока (и, как следствие, "заморозки" пользовательского интерфейса приложения), все рекурсивные вычисления фрактала Мандельброта выполняются в отдельном потоке. Поток испускает сигнал, когда он заканчивает рисовать фрактал. Точно также, в примере "Blocking Fortune Client" используется отдельный поток для асинхронной связи с TCP-сервером. |
Попытка перевода Qt документации. Если есть желание присоединиться, или если есть замечания или пожелания, то заходите на форум: Перевод Qt документации на русский язык... Люди внесшие вклад в перевод: Команда переводчиков |