Utiliser correctement QSqlDatabase dans les programmes multi-threads
Une connexion ne peut être utilisé dans le thread qui l'a créé. Le déplacement des connexions entre les threads ou la création de requêtes à partir d'un thread différent n'est pas supporté.
la question qui me dérange, c'est ce qui se passe quand je copie une instance de base de données. Pour exemple, voici le code dans le thread principal:
int main(int argc, char** argv) {
...
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "DB1");
db.setHostName("localhost");
...
<!-Et voici le lien dans worker threads:
void MyThread::run() {
QSqlDatabase db(QSqlDatabase::database("DB1"));
if (db.open()) {
...
}
<!-Ce fil est-il sûr ou non? Normalement, une telle opération serait sûre en C++, mais puisque QT utilise le partage implicite et l'affinité de thread, Je ne suis plus sûr.
Ils disent: une connexion ne peut être utilisée que depuis le thread qui l'a créée, mais qu'est-ce que cela signifie? Is Qsqlddatabase::addDatabase point where connexion est créé ou il fait quand open () la fonction est appelé.
mise à jour:
après la réponse de Laszlo Papp, et éventuellement en regardant dans le code source Qt, je dois dire que la conception de cette partie de Qt semble défectueuse pour moi.
si je comprends bien, QSqlDatabase utilise le partage implicite sous le capot, mais malheureusement ce n'est pas un vrai partage implicite, car le constructeur de copie de l'instance QSqlDatabase ne créera pas une nouvelle instance de données partagées quand elle est nécessaire. Pour aggraver les choses, vous ne pouvez pas créer de connexion temporaire, mais vous devez utiliser des méthodes statiques addDatabase/removeDatabase, auquel cas vous devez synchroniser les threads pour éviter la collision de noms.
cela rend bien sûr l'utilisation de QSqlDatabase dans QtConcurrent très difficile, surtout si la connexion doit être enfouie profondément derrière une certaine abstraction. Puisque nous ne savons pas sur quel thread de code va s'exécuter, nous ne pouvons pas garder la connexion ouverte entre les deux appels. Et si nous voulions engendrer un nombre dynamique de tâche, nous devrions nous assurer que les tâches n'utilisent pas le même nom de base de données.
tout cela me fait me poser des questions sur les objectifs de conception et si le partage implicite est approprié pour ce cas particulier. IMHO, beaucoup mieux solution serait de laisser le constructeur de copie faire vraiment le travail et faire une copie de connexion pour vous. Ceux qui ne veulent pas de copies privées/temporaires, peuvent toujours utiliser addDatebase / removeDatabase, auquel cas la base de données de méthode () doit être modifiée pour retourner référence.
2 réponses
ils disent: une connexion ne peut être utilisée que depuis le fil qui l'a créée, mais qu'est-ce que cela signifie? Est-ce que QSqlDatabase:: addDatabase point où la connexion est créée ou bien quand la fonction open() est appelée.
le premier. Voir le documentation pour plus de détails:
La classe QSqlDatabase représente une connexion à une base de données.
La classe QSqlDatabase fournit une interface pour accéder à la base de données grâce à une connexion. Une instance de QSqlDatabase représente la connexion. La connexion fournit l'accès à la base de données via l'un des pilotes de base de données pris en charge, qui sont dérivés de QSqlDriver. Vous pouvez aussi classer votre propre pilote de base de données à partir de QSqlDriver. Voir comment écrire votre propre pilote de base de données pour plus d'informations.
créer une connexion (i.e., une instance de QSqlDatabase) en appelant l'une des adddatabases statiques() fonction...
Cette dernière phrase devrait vider votre préoccupation.
Vous pouvez utiliser QSqlDatabase::cloneDatabase
pour obtenir une" vraie " copie d'une base de données, qui est prête à être ouverte dans n'importe quel thread.
Vous devez faire cela dans le thread qui a initialisé la base de données clonée, mais vous pouvez déplacer la base de données obtenue non encore ouverte à n'importe quel thread et travailler avec elle là.