Могу ли я использовать взаимодействие с данными QAbstractTableModel в QThread?

Я знаю, что мы не можем использовать GUI-взаимодействие в не-GUI потоках (QThread). Но я не знаю, можем ли мы взаимодействовать с моделью (QAbstractItemModel) в темах и если True, то как это сделать правильно?

Я честно искал что-то об этом в Google и на SO, и, похоже, нет соответствующих ответов на мой вопрос.

1 ответ

Решение

Что вы подразумеваете под "взаимодействовать с моделью"? Если вы имеете в виду, что вы хотите получить доступ к модели из нескольких потоков, напрямую манипулируя ею, то вы должны сериализовать доступ к модели. Поскольку в модели так много методов, я бы посоветовал вам не добавлять мьютекс в модель - это было бы очень утомительно и подвержено ошибкам, так как было бы слишком легко забыть блокировщик мьютекса. Вместо этого используйте тот факт, что ваша модель наследует QObject и, таким образом, может принимать события.

  1. Ваш поток графического интерфейса напрямую обращается к модели.
  2. Другие потоки взаимодействуют с моделью, публикуя в ней события (и, возможно, получая ответные события).
  3. Поток графического интерфейса будет обрабатывать эти события последовательно с любым другим доступом, таким образом защищая вашу модель от одновременного доступа.

Другие потоки могут, конечно, получать ответы от модели - также через события. У вас будет два базовых класса событий: Request класс, который используется для запроса вещей из модели, а затем будет Response базовый класс событий, который модель будет использовать для ответа. Класс Request должен иметь QObject* sender член, так что модель будет знать, в какой объект QObject отправлять событие ответа. Вы, вероятно, захотите, чтобы и запрос, и ответ имели одинаковый идентификатор (скажем, последовательное увеличение int), чтобы запросы и ответы могли совпадать.

Вы должны реализовать весь многопоточный код, который взаимодействует с моделью через события, а не путем переопределения QThread::run(), но внутри QObject. После того, как вы создаете QObjectПросто перенесите его в отдельную ветку. Реализация QThread по умолчанию run() будет вращать цикл обработки событий, чтобы ваш QObject выполнялся, если к нему есть какие-либо события, сигналы или таймеры. Таймер нулевой длительности - это способ постоянно держать поток занятым, но убедитесь, что вы не выполняете слишком много обработки за один раз, иначе вы задержите обработку входящих событий.

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

  1. connect() им,
  2. вызвать их через QMetaObject::invokeMethod с Qt::QueuedConnection,
  3. вызывать их через функтор (скажем, лямбду), выполняемый в контексте основного потока; посмотрите этот ответ, чтобы узнать, как это сделать.

За кулисами, когда вы подключаете сигналы к слотам объектов QObject, которые находятся в отдельных потоках, Qt создает соединение, которое маршалирует каждый сигнал в QMetaCallEvent, а затем демарширует его в потоке, в котором находится объект QObject с целевым слотом.

Другие вопросы по тегам