QSqlDatabase在多线程中的使用

Qt中多线程使用数据库_qt数据库管理类支持多数据库,多线程-CSDN博客

1.

代码:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPushButton>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlDriver>
#include <QThread>
#include <QTextBrowser>
#include <QVBoxLayout>

class Test : public QObject
{
    Q_OBJECT
public:
    Test(QSqlDatabase database)
        :   QObject(nullptr), m_database(database)
    {
    }
signals:
    void query_result(const QString &result);
public slots:
    void test_query()
    {
        m_database.open();
        QString str = "Thread ID: " + QString::number((int)QThread::currentThreadId(), 16) + "  result:";
        QSqlQuery query(m_database);
        query.exec("SELECT * FROM test;");
        while(query.next())
            str += query.value(0).toString() + "|";
        m_database.close();
        emit query_result(str + "\n");
    }
private:
    QSqlDatabase m_database;
};

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr)
        :   QWidget(parent)
    {
        QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "mydb");
        db.setPort(3306);
        db.setDatabaseName("test");
        db.setUserName("root");
        db.setPassword("root_pwd");

        test1 = new Test(db);
        test2 = new Test(db);

        test1->moveToThread(&thread1);
        test2->moveToThread(&thread2);

        thread1.start();
        thread2.start();

        QPushButton *button = new QPushButton("开始", this);
        QTextBrowser *browser = new QTextBrowser(this);
        browser->insertPlainText("UI Thread ID: " + QString::number((int)QThread::currentThreadId(), 16));
        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(button);
        layout->addWidget(browser);
        setLayout(layout);

        connect(button, &QPushButton::clicked, this, &Widget::start);
        connect(button, &QPushButton::clicked, this, [=]()
        {
           browser->insertPlainText("\n");
        });
        connect(&thread1, &QThread::finished, &thread1, &QThread::deleteLater);
        connect(&thread2, &QThread::finished, &thread2, &QThread::deleteLater);
        connect(test1, &Test::query_result, this, [=](const QString &result)
        {
           browser->insertPlainText(result);
        });
        connect(test2, &Test::query_result, this, [=](const QString &result)
        {
           browser->insertPlainText(result);
        });

        resize(500, 400);
    }

    ~Widget()
    {
        thread1.wait();
        thread2.wait();
    }

public slots:
    void start()
    {
        QMetaObject::invokeMethod(test1, "test_query");
        QMetaObject::invokeMethod(test2, "test_query");
    }

private:
    QThread thread1;
    QThread thread2;

    Test *test1;
    Test *test2;
};

#endif // WIDGET_H

数据库: 

1000条记录:内容均为123

ui: 

 

有一个线程中的数据库没有打开

 ------

2.

修改代码:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPushButton>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlDriver>
#include <QThread>
#include <QTextBrowser>
#include <QVBoxLayout>
#include <QDebug>
class Test : public QObject
{
    Q_OBJECT
public:
    Test(QSqlDatabase database)
        :   QObject(nullptr), m_database(database)
    {
    }
signals:
    void query_result(const QString &result);
public slots:
    void test_query()
    {
        qDebug()<<__FILE__<<"["<<__LINE__<<"]"<<m_database;
        m_database.open();
        qDebug()<<__FILE__<<"["<<__LINE__<<"]"<<m_database;
        QString str = "Thread ID: " + QString::number((int)QThread::currentThreadId(), 16) + "  result:";
        QSqlQuery query(m_database);
        query.exec("SELECT * FROM test;");
        while(query.next())
            str += query.value(0).toString() + "|";
        m_database.close();
        emit query_result(str + "\n");
    }
private:
    QSqlDatabase m_database;
};

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr)
        :   QWidget(parent)
    {
        QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "mydb000");
        db.setPort(3306);
        db.setDatabaseName("test");
        db.setUserName("root");
        db.setPassword("root_pwd");

        QSqlDatabase db1 = QSqlDatabase::addDatabase("QMYSQL", "mydb111");
        db1.setPort(3306);
        db1.setDatabaseName("test");
        db1.setUserName("root");
        db1.setPassword("root_pwd");

        test1 = new Test(db);
        test2 = new Test(db1);

        test1->moveToThread(&thread1);
        test2->moveToThread(&thread2);

        thread1.start();
        thread2.start();

        QPushButton *button = new QPushButton("开始", this);
        QTextBrowser *browser = new QTextBrowser(this);
        browser->insertPlainText("UI Thread ID: " + QString::number((int)QThread::currentThreadId(), 16));
        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(button);
        layout->addWidget(browser);
        setLayout(layout);

        connect(button, &QPushButton::clicked, this, &Widget::start);
        connect(button, &QPushButton::clicked, this, [=]()
        {
           browser->insertPlainText("\n");
        });
        connect(&thread1, &QThread::finished, &thread1, &QThread::deleteLater);
        connect(&thread2, &QThread::finished, &thread2, &QThread::deleteLater);
        connect(test1, &Test::query_result, this, [=](const QString &result)
        {
           browser->insertPlainText(result);
        });
        connect(test2, &Test::query_result, this, [=](const QString &result)
        {
           browser->insertPlainText(result);
        });

        resize(500, 400);
    }

    ~Widget()
    {
        thread1.wait();
        thread2.wait();
    }

public slots:
    void start()
    {
        QMetaObject::invokeMethod(test1, "test_query");
        QMetaObject::invokeMethod(test2, "test_query");
    }

private:
    QThread thread1;
    QThread thread2;

    Test *test1;
    Test *test2;
};

#endif // WIDGET_H

不同的线程中使用不同的连接名:

发现还是不可以成功访问。

debug\../../demo/widget.h [ 27 ] QSqlDatabase(driver="QMYSQL", database="test", host="", port=3306, user="root", open=false)

debug\../../demo/widget.h [ 27 ] QSqlDatabase(driver="QMYSQL", database="test", host="", port=3306, user="root", open=false)

debug\../../demo/widget.h [ 29 ] QSqlDatabase(driver="QMYSQL", database="test", host="", port=3306, user="root", open=false)

QSqlQuery::exec: database not open

debug\../../demo/widget.h [ 29 ] QSqlDatabase(driver="QMYSQL", database="test", host="", port=3306, user="root", open=true)

只有一个数据库可以打开。。。

3.

Qt多线程操作MySql数据库 - 补码 - 博客园 (cnblogs.com)

看了看这篇文章,在

QSqlDatabase::addDatabase和QSqlDatabase::open外部加锁,然后两个数据库都可以打开了。

(应该是在执行这两个函数时,会有数据竞争的情况发生)

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPushButton>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlDriver>
#include <QThread>
#include <QTextBrowser>
#include <QVBoxLayout>
#include <QDebug>
#include <QMutex>

extern QMutex mutex;
class Test : public QObject
{
    Q_OBJECT
public:
    Test(const QString &connectName)
        :   QObject(nullptr)
    {
        mutex.lock();
        m_database = QSqlDatabase::addDatabase("QMYSQL",connectName);
        m_database.setHostName("127.0.0.1");
        m_database.setPort(3306);
        m_database.setDatabaseName("test");
        m_database.setUserName("root");
        m_database.setPassword("root_pwd");
        mutex.unlock();
    }
signals:
    void query_result(const QString &result);
public slots:
    void test_query()
    {
        qDebug()<<__FILE__<<"["<<__LINE__<<"]"<<m_database;
        mutex.lock();
        if(m_database.open())
        {
            qDebug()<<__FILE__<<"["<<__LINE__<<"]"<<m_database;
            QString str = "Thread ID: " + QString::number((int)QThread::currentThreadId(), 16) + "  result:";
            QSqlQuery query(m_database);
            query.exec("SELECT * FROM test;");
            while(query.next())
                str += query.value(0).toString() + "|";
            m_database.close();
            emit query_result(str + "\n");
        }
        else
        {
            qDebug()<<__FILE__<<"["<<__LINE__<<"]"<<m_database.lastError();
        }
        mutex.unlock();
    }
private:
    QSqlDatabase m_database;
};

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr)
        :   QWidget(parent)
    {
        test1 = new Test("db");
        test2 = new Test("db1");

        test1->moveToThread(&thread1);
        test2->moveToThread(&thread2);

        thread1.start();
        thread2.start();

        QPushButton *button = new QPushButton("开始", this);
        QTextBrowser *browser = new QTextBrowser(this);
        browser->insertPlainText("UI Thread ID: " + QString::number((int)QThread::currentThreadId(), 16));
        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(button);
        layout->addWidget(browser);
        setLayout(layout);

        connect(button, &QPushButton::clicked, this, &Widget::start);
        connect(button, &QPushButton::clicked, this, [=]()
        {
           browser->insertPlainText("\n");
        });
        connect(&thread1, &QThread::finished, &thread1, &QThread::deleteLater);
        connect(&thread2, &QThread::finished, &thread2, &QThread::deleteLater);
        connect(test1, &Test::query_result, this, [=](const QString &result)
        {
           browser->insertPlainText(result);
        });
        connect(test2, &Test::query_result, this, [=](const QString &result)
        {
           browser->insertPlainText(result);
        });

        resize(500, 400);
    }

    ~Widget()
    {
        thread1.wait();
        thread2.wait();
    }

public slots:
    void start()
    {
        QMetaObject::invokeMethod(test1, "test_query");
        QMetaObject::invokeMethod(test2, "test_query");
    }

private:
    QThread thread1;
    QThread thread2;

    Test *test1;
    Test *test2;
};

#endif // WIDGET_H
#include <QApplication>
#include "widget.h"

QMutex mutex;
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

debug\../../demo/widget.h [ 38 ] QSqlDatabase(driver="QMYSQL", database="test", host="127.0.0.1", port=3306, user="root", open=false)

debug\../../demo/widget.h [ 38 ] QSqlDatabase(driver="QMYSQL", database="test", host="127.0.0.1", port=3306, user="root", open=false)

debug\../../demo/widget.h [ 42 ] QSqlDatabase(driver="QMYSQL", database="test", host="127.0.0.1", port=3306, user="root", open=true)

debug\../../demo/widget.h [ 42 ] QSqlDatabase(driver="QMYSQL", database="test", host="127.0.0.1", port=3306, user="root", open=true)

 ---

在使用多线程时,特别需要注意的就是刚才出现的情况。

如:有两个线程,它们同时要访问一个共有的资源,这时候就会出现对资源的竞争,如果不添加锁,会出现什么情况将是未知的。

比如上面的例子:

m_database.open()

线程1打开数据库成功,它的流程正常进行。

而线程2打开数据库失败,此时让流程继续走下去,结果不是我们期望的,所以要加锁。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/886152.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习】05-Rnn循环神经网络-01- 自然语言处理概述/词嵌入层/循环网络/文本生成案例精讲

循环神经网络&#xff08;RNN&#xff09;主要用于自然语言处理的。 循环神经网络&#xff08;RNN&#xff09;、卷积神经网络&#xff08;CNN&#xff09;和全连接神经网络&#xff08;FCN&#xff09;是三种常见的神经网络类型&#xff0c;各自擅长处理不同类型的数据。下面…

RabbitMQ应用

RabbitMQ 共提供了7种⼯作模式, 进⾏消息传递 一、七种模式的概述 1、Simple(简单模式) P:生产者,就是发送消息的程序 C:消费者,就是接收消息的程序 Queue:消息队列,类似⼀个邮箱, 可以缓存消息; ⽣产者向其中投递消息, 消费者从其中取出消息 特点: ⼀个⽣产者P,⼀…

负载均衡--相关面试题(六)

在负载均衡的面试中&#xff0c;可能会遇到一系列涉及概念、原理、实践应用以及技术细节的问题。以下是一些常见的负载均衡面试题及其详细解答&#xff1a; 一、什么是负载均衡&#xff1f; 回答&#xff1a;负载均衡是一种将网络请求或数据传输工作分配给多个服务器或网络资源…

SpringSession微服务

一.在linux中确保启动起来redis和nacos 依赖记得别放<dependencyManagement></dependencyManagement>这个标签去了 1.首先查看已经启动的服务 docker ps 查看有没有安装redis和nacos 2.启动redis和nacos 发现没有启动redis和nacos,我们先来启动它。&#xff0c;…

计算机视觉学习路线:从基础到进阶

计算机视觉学习路线&#xff1a;从基础到进阶 计算机视觉&#xff08;Computer Vision&#xff09;是人工智能和机器学习领域中重要的分支&#xff0c;致力于让计算机能够理解和分析图像、视频等视觉信息。随着深度学习的发展&#xff0c;计算机视觉的应用变得越来越广泛&…

音视频入门基础:FLV专题(7)——Tag header简介

一、引言 从《音视频入门基础&#xff1a;FLV专题&#xff08;3&#xff09;——FLV header简介》中可以知道&#xff0c; 在FLV header之后&#xff0c;FLV文件剩下的部分应由PreviousTagSize和Tag组成。FLV文件 FLV header PreviousTagSize0 Tag1 PreviousTagSize1 Ta…

【C++】“list”的介绍和常用接口的模拟实现

【C】“list”的介绍和常用接口的模拟实现 一. list的介绍1. list常见的重要接口2. list的迭代器失效 二. list常用接口的模拟实现&#xff08;含注释&#xff09;三. list与vector的对比 一. list的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xf…

2025 年 IT 前景:机遇与挑战并存,人工智能和云计算成重点

云计算de小白 投资人工智能&#xff1a;平衡潜力与实用性 到 2025 年&#xff0c;人工智能将成为 IT 支出的重要驱动力&#xff0c;尤其是在生成式人工智能领域。人工智能的前景在于它有可能彻底改变业务流程、增强决策能力并开辟新的收入来源。然而&#xff0c;现实情况更加微…

SpringCloud源码:服务端分析(二)- EurekaServer分析

背景 从昨日的两篇文章&#xff1a;SpringCloud源码&#xff1a;客户端分析&#xff08;一&#xff09;- SpringBootApplication注解类加载流程、SpringCloud源码&#xff1a;客户端分析&#xff08;二&#xff09;- 客户端源码分析。 我们理解了客户端的初始化&#xff0c;其实…

Python画笔案例-071 绘制闪闪的红星

1、绘制通闪闪的红星 通过 python 的turtle 库绘制 闪闪的红星,如下图: 2、实现代码 绘制闪闪的红星,以下为实现代码: """闪闪的红星.py """ import time import turtledef xsleep(n):"""防

通信工程学习:什么是MAC媒体接入控制

MAC&#xff1a;媒体接入控制 MAC&#xff08;Medium Access Control&#xff09;&#xff0c;即媒体接入控制&#xff0c;是计算机网络中数据链路层的一个重要组成部分&#xff0c;负责协调多个发送和接收站点对一个共享传输媒体的占用。以下是关于MAC的详细解释&#xff1a; …

系统架构设计师-知识产权与标准化

目录 一、保护范围与对象 二、保护期限 三、知识产权人确定 四、侵权判断 五、标准化 一、保护范围与对象 知识产权是权利人依法就下列课题享有的专有权利&#xff1a; &#xff08;一&#xff09;作品&#xff08;著作&#xff09; &#xff08;二&#xff09;发明、实用…

泰勒图 ——基于相关性与标准差的多模型评价指标可视化比较-XGBoost、sklearn

1、基于相关性与标准差的多模型评价指标可视化比较 # 数据读取并分割 import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split plt.rcParams[font.family] = Times New Roman plt.rcParams[axes.unic…

针对考研的C语言学习(2019链表大题)

题目解析&#xff1a; 【考】双指针算法&#xff0c;逆置法&#xff0c;归并法。 解析&#xff1a;因为题目要求空间复杂度为O(1)&#xff0c;即不能再开辟一条链表&#xff0c;因此我们只能用变量来整体挪动原链表。 第一步先找出中间节点 typedef NODE* Node; Node find_m…

Linux-基础篇-磁盘分区,挂载

Linux 分区 原理介绍 Linux 来说无论有几个分区&#xff0c;分给哪一目录使用&#xff0c;它归根结底就只有一个根目录&#xff0c;一个独立且唯一的文件结构 , Linux 中每个分区都是用来组成整个文件系统的一部分。 Linux 采用了一种叫 “ 载入 ” 的处理方法&#xff0c;…

为什么有必要由母语人士翻译应用程序界面

在当今技术已成为我们生活不可或缺的一部分的世界中&#xff0c;移动应用接口在我们与数字空间的互动中发挥着关键作用。然而&#xff0c;无论应用程序本身多么完美&#xff0c;它的有效性可能会因糟糕地翻译而大大降低。这就是为什么&#xff0c;为了翻译应用程序界面&#xf…

在线css像素px到Em的转换器

具体请前往&#xff1a;在线Px转Em工具--将绝对像素(px)长度单位转换为相对长度em

Android SystemUI组件(09)唤醒亮屏 锁屏处理流程

该系列文章总纲链接&#xff1a;专题分纲目录 Android SystemUI组件 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节持续迭代之前章节的思维导图&#xff0c;主要关注左侧上方锁屏分析部分 唤醒亮屏 即可。 Power按键的处理逻辑最终是由PhoneWindowManager来…

VMware ESXi Centos7网卡名称 ens192 变更eth0

1.在 /etc/sysconfig/network-scirpts/ 文件夹下 创建一个ifcfg-eth0的文件&#xff0c; 最简单的方式是 mv ifcfg-ens192 ifcfg-eth0 然后 vi ifcfg-eth0 把DEVICE改成 DEVICEeth0 wq! 保存 2. vi /etc/sysconfig/grub # 在位置添加 net.ifnames0 biosdevname0 参数 完…

了解芯片光刻与OPC

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 参考资料&#xff1a; 光刻技术与基本流程 https://www.bilibili.com/video/BV1tP4y1j7BA OPC https://www.bilibili.com/video/BV1o94y1U7Td 论文&#xff1a;计算…