Words are DUST

вторник, 11 марта 2014 г.

Qt основы многопоточности

Правильная реализация многопоточности Qt

В старых учебниках по Qt (того же Шлее) указывается, что для создания потока, необходимо унаследоваться от класса QThread, перегрузив виртуальный метод void run(), в духе Java. Проблема в том, что с некоторого времени класс QThread перестал быть чисто виртуальным (абстрактным), что уже не требует реализации метода run() и какого-либо наследования. Специалисты и документация утверждают, что QThread является вполне себе интерфейсом к объекту потока, и что наследование от него обязательно приведет к беде при использовании сигналов и слотов базового класса.

Решение проблемы

Предлагается использовать некий сторонний класс (его обычно зовут Worker и наследуют от QObject для использования сигналов со слотами) со слотовым методом, который стоит передать в созданный объект потока (QObject::moveToThread(QThread)) и связать тот слотовой метод с сигналом started() потока.

Создаем и запускаем поток:

То есть, у нас есть класс Worker в новом потоке, управляющий этим-самым потоком через сигналы. В данном случае он приказывает ему завершиться, когда сделает свою работу (высылает сигнал finished в конце метода process, который становится, в этом качестве, аналогом функции main для отдельного объекта потока QThread).

Увы, просто так это не попробуешь, потому что основной поток все норовит прекратить свое существование, попутно прибивая дочерние потоки, нохально не обращая внимание, что они там чем-то заняты. Можно устроить ему огромный тайм-аут через QThread::getCurrentThread()-sleep(время_в_секундах), примерно оценив необходимости. Можно создать объект QApplication и запустить его вечный цикл обработки сообщений. Но как потом вызвать его слот quit()? Это все не круто. Нам нужно знать, когда потоки «кончатся», а лишь потом завершать приложение.

Велосипеды

Будем пилить менеджер потоков, который будет создавать потоки, уметь их опрашивать и, в конце концов, сообщать объекту приложения QApplication, что ждать больше нечего, можно умирать.

Совместное использование данных и мьютексы

Краткое отступление по теме синхронизации доступа к данным из разных потоков. Дело в том, что потоки, в отличае от процессов, имеют доступ к одному и тому же адресному пространству (в рамках процесса, в котором они созданы), следовательно, и ко всем переменным, даже локальным. Представьте ситуацию, один поток находится в методе, в начале инициализировал переменные, затем накопил в них какой-то результят, который готовится вернуть. Тут, внезапно, в метод врывается другой поток, снова инициализирует переменные и начинает в них копить свои результаты. Первый поток из метода выносит ошибочный результат. Эта ситуация зовется «Data races». Требуется синхронизация, т.е. заставить второй поток ждать, пока первый не закончит свое дело. Здесь помогут примитивы синхронизации: критические секции, мьютексы, симофоры и другие. Сейчас о мьютексах. Это такой объект, которым может владеть только один поток. Второй поток, попытавшись захватить мьютекс, должен ждать, пока тот не освободится. В Qt мьютекс представлен классом QMutex, имеет два важных метода захвата и освобождения — lock() и unlock() соответственно. Например, заходя в критическую область, поток вызывает lock(), делает свое черное дело, вызывает unlock(). Далее будет пример работы с менеджером потоков, в котором будут хранится указатели на потоки, проверяться их состояние, также там будет общий для них мьютекс. Потоки будут бороться за доступ к выводу на консоль.

Также здесь в методе объект ThreadPool::updateStatus() создается объект QMutexLocker, который автоматически вызывает lock() при конструировании и unlock() при уничтожении (при выходе из области видимости) мьютекса, указатель на который передается в конструктор. Кроме того, ThreadPool при завершении каждого потока проверяет статус остальных, и, в том случае, если все завершились, высылает сигнал allFinished, который и позволяет завершиться обработке сообщений объекта QApplication (см. ф-ю main).

Источник:http://words-are-dust.blogspot.ru/2014/03/qt.html