Правильное использование QSqlDatabase в многопоточных программах

Основываясь на документации Qt:

Соединение может использоваться только из потока, который его создал. Перемещение соединений между потоками или создание запросов из другого потока не поддерживается.

Вопрос, который меня беспокоит, заключается в том, что происходит, когда я копирую-конструирую экземпляр базы данных. Например, вот код в основном потоке:

int main(int argc, char** argv) {
...
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "DB1");
db.setHostName("localhost");
...

а вот связь в рабочем потоки:

void MyThread::run() {
QSqlDatabase db(QSqlDatabase::database("DB1"));
if (db.open()) {
...
}

Эта тема безопасна или нет? Обычно такая операция была бы безопасна в C ++, но, поскольку QT использует неявное совместное использование и привязку потоков, я больше не уверен.

Они говорят: Соединение может использоваться только из потока, который его создал, Но что это значит? QSqlDatabase :: addDatabase указывает, где соединение создается или это на самом деле, когда открыть() функция называется.

ОБНОВИТЬ:

После ответа от Laszlo Papp и, в конце концов, изучив исходный код Qt, я должен сказать, что дизайн этой части Qt выглядит мне некорректно.

Если я правильно понимаю, QSqlDatabase использует неявный общий доступ, но, к сожалению, это не настоящий неявный общий доступ, так как конструктор копирования экземпляра QSqlDatabase не будет создавать новый экземпляр общих данных, когда это необходимо. Что еще хуже, вы не можете создать временное соединение, но вместо этого вы должны использовать статические методы addDatabase / removeDatabase, в этом случае вам нужно синхронизировать потоки, чтобы избежать конфликта имен.

Это, конечно, делает использование QSqlDatabase в QtConcurrent очень сложным, особенно если соединение должно быть скрыто глубоко за некоторой абстракцией. Так как мы не знаем, какой код потока будет запущен, мы не можем держать соединение открытым между двумя вызовами. И если мы хотим создать динамический номер задачи, нам нужно убедиться, что задачи не используют одно и то же имя базы данных.

Все это заставляет меня задуматься о целях проектирования и о том, подходит ли неявное совместное использование для этого конкретного случая. ИМХО, гораздо лучшим решением было бы позволить конструктору копирования действительно сделать свою работу и сделать копию соединения для вас. Те, кто не хочет иметь частные / временные копии, могут по-прежнему использовать addDatebase / removeDatabase, и в этом случае метод database () необходимо изменить, чтобы он возвращал ссылку.

15

Решение

Они говорят: соединение может использоваться только из потока, который его создал, но что это значит? Является ли QSqlDatabase :: addDatabase точкой, где создается соединение, или оно фактически при вызове функции open ().

Бывший. Увидеть документация для деталей:

Класс QSqlDatabase представляет соединение с базой данных.

Класс QSqlDatabase предоставляет интерфейс для доступа к базе данных через соединение. Экземпляр QSqlDatabase представляет соединение. Соединение обеспечивает доступ к базе данных через один из поддерживаемых драйверов базы данных, которые получены из QSqlDriver. Кроме того, вы можете создать подкласс своего собственного драйвера базы данных из QSqlDriver. См. Как написать свой собственный драйвер базы данных для получения дополнительной информации.

Создайте соединение (например, экземпляр QSqlDatabase), вызвав одну из статических функций addDatabase () …

Это последнее предложение должно прояснить вашу озабоченность.

5

Другие решения

Ты можешь использовать QSqlDatabase::cloneDatabase для получения «реальной» копии базы данных, которая готова к открытию в любом потоке.

Это необходимо сделать в потоке, который инициализировал клонированную базу данных, но вы можете переместить полученную еще не открытую базу данных в любой поток и поработать с ней там.

4

По вопросам рекламы [email protected]