多线程访问数据库问题

    最近做一个多线程的远程升级软件,做完后用一个对应的测试程序进行测试,发现线程一多必崩溃,而把所有数据库处理全部删掉后,就可以同时运行几百个线程不崩溃了.原因应该是自己采用了单例模式,在各个线程中发送信号给TcpServer,在server中统一单线程调用SqliteTool的一个单例操作数据库,原以为这样应该是更安全和高效的,现在看应该用多线程加静态函数的方式了.

2018/3/23更新:

    1.单例模式不要用了,多线程访问一个对象就是坑.Java下的连接池管理对象是单例,但是连接池对象是多个的.所以静态函数就可以了

#include "dbtool.h"
#include <QApplication>
#include <QDebug>
#include <QSqlQuery>
#include <QSqlError>
#include <QThread>

QMutex DBTool::mutex4Thread;
DBTool::DBTool()
{
    qDebug()<<"DBTool()";
}

DBTool::~DBTool()
{
    qDebug()<<"DBTool()";
}

QSqlDatabase DBTool::getConnection()
{
    QSqlDatabase db;
    QString dbType="QSQLITE";
    QString connectionName=QString::number((quint64)(QThread::currentThread()),16);
    QString dbName=QCoreApplication::applicationDirPath()+"/db/sqlite3.db";
    if(QSqlDatabase::contains(connectionName))
    {
        qDebug()<<"ERROR:db=QSqlDatabase::database(connectionName);";
        db=QSqlDatabase::database(connectionName);
    }
    else
    {
        db=QSqlDatabase::addDatabase(dbType,connectionName);
    }
    db.setDatabaseName(dbName);
    if(!db.open())
    {
        qDebug()<<"open error"<<db.lastError().text();
    }
    return db;
}

void DBTool::removeConnection()
{
    QString connectionName=QString::number((quint64)(QThread::currentThread()),16);
    if(QSqlDatabase::contains(connectionName))
    {
        QSqlDatabase::removeDatabase(connectionName);
    }
}

使用的时候一要加锁,必须加,二QSqlDatabase对象搞个局部的,然后remove

void TcpThread::db_progress(double progress)
{
    QMutexLocker locker(&DBTool::mutex4Thread);
    QString progressStr=QString::number(progress*100,'f',2)+"%";
    {
        QSqlDatabase db=DBTool::getConnection();
        QSqlQuery query(db);
        query.prepare("UPDATE table_all set progress = :progress WHERE id LIKE :id");
        query.bindValue(":progress",progressStr);
        query.bindValue(":id",_id);
        if(!query.exec())
        {
            qDebug()<<"query exec error5:"<<query.lastError().text();
        }
    }
    DBTool::removeConnection();
}

    2.更新数据库界面,Query操作放到子线程里区,不要在主线程做耗时操作

#include "sql4ui.h"
#include <QSqlDatabase>
#include <QSqlQuery>
#include "tool/dbtool.h"
#include <QVariant>
#include <QVector>
#include <QMetaObject>
#include "global.h"
#include <QDebug>

Sql4ui::Sql4ui(QObject *obj, int request, int page_num, QString filter_sql, QString query_filter) : QRunnable()
{
    _obj=obj;
    _request=request;
    _page_num=page_num;
    _filter_sql=filter_sql;
    _query_filter=query_filter;
}

Sql4ui::~Sql4ui()
{
}

void Sql4ui::run()
{
    if(_request==0) //更新界面
    {
        refresh_table();
    }
    else if(_request==1)    //根据条件查询数目
    {
        query_count();
    }
}

void Sql4ui::refresh_table()
{
    {
        QSqlDatabase db=DBTool::getConnection();
        int count=0;
        //QString cells[ROW_OF_TABLE][COLUMN_OF_TABLE];
        //使用[]要先resize
        QVector<QVector<QString>> cells(ROW_OF_TABLE);
        for(int i=0;i<ROW_OF_TABLE;i++)
        {
            QVector<QString> vec(COLUMN_OF_TABLE);
            cells[i]=vec;
        }
        {
            QMutexLocker locker(&DBTool::mutex4Thread);
            //查询符合where_sql的记录总数count
            QSqlQuery query1("SELECT count(*) FROM table_all"+_filter_sql,db);
            if(query1.next())
            {
                count=query1.value(0).toInt();
            }
            QSqlQuery query(db);
            int start=(_page_num-1)*ROW_OF_TABLE;
            if(_filter_sql!="")
            {
                query.prepare("SELECT * FROM table_all "+_filter_sql+" ORDER BY id");
                if(query.exec())
                {
                    int rowIndex=0;
                    if(query.seek(start))
                    {
                        bool is_next_ok=true;
                        while(is_next_ok&&rowIndex<ROW_OF_TABLE)
                        {
                            cells[rowIndex][0]=query.value("id").toString();
                            cells[rowIndex][1]=query.value("SA").toString();
                            cells[rowIndex][2]=query.value("status").toString();
                            cells[rowIndex][3]=query.value("progress").toString();
                            cells[rowIndex][4]=query.value("error").toString();
                            cells[rowIndex][5]=query.value("datetime").toString();
                            rowIndex++;
                            is_next_ok=query.next();
                        }
                    }
                }
            }
            else    //无筛选大数据时这样更快
            {
                int end=_page_num*ROW_OF_TABLE;
                query.prepare("SELECT * FROM table_all WHERE id > :start AND id <= :end");
                query.bindValue(":start",start);
                query.bindValue(":end",end);
                if(query.exec())
                {
                    int rowIndex=0;
                    while(query.next())
                    {
                        cells[rowIndex][0]=query.value("id").toString();
                        cells[rowIndex][1]=query.value("SA").toString();
                        cells[rowIndex][2]=query.value("status").toString();
                        cells[rowIndex][3]=query.value("progress").toString();
                        cells[rowIndex][4]=query.value("error").toString();
                        cells[rowIndex][5]=query.value("datetime").toString();
                        rowIndex++;
                    }
                }
            }
        }
        QMetaObject::invokeMethod(_obj,"invokeTable",Qt::QueuedConnection,Q_ARG(int,count),Q_ARG(QVector<QVector<QString>>,cells));
    }
    DBTool::removeConnection();
}

void Sql4ui::query_count()
{
    {
        QSqlDatabase db=DBTool::getConnection();
        int count=0;
        {
            QMutexLocker locker(&DBTool::mutex4Thread);
            QSqlQuery query(db);
            query.prepare("SELECT count(*) FROM table_all "+_query_filter);
            if(query.exec())
            {
                if(query.next())
                {
                    count=query.value(0).toInt();
                }
            }
        }
        QMetaObject::invokeMethod(_obj,"invokeQueryCount",Qt::QueuedConnection,Q_ARG(int,count));
    }
    DBTool::removeConnection();
}

引用

    

Qt数据库由QSqlDatabase::addDatabase()生成的QSqlDatabase只能在创建它的线程中使用, 在多线程中共用连接或者在另外一个线程中创建query都是不支持的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
假设有如下代码:
bool  openDatabase()
{
     QSqlDatabase db;
     QString connectionName =  "sqlite" ;
     db = QSqlDatabase::addDatabase( "QSQLITE" , connectionName);
     db.setDatabaseName( "/jyxtec.db" );
     if  (db.open())         return  true ;
     else     return  false ;
}
void  testQuery()
{
     QSqlQuery query(QSqlDatabase::database( "sqlite" ));
     query.exec( "SELECT * from t_test" );   
}

这里的testQuery()是不支持多线程调用的,只能在调用OpenDatabase()的线程中使用.否则很容易段错误。
解决方法有两种:
1)每个调用testQuery的线程中创建不同connectionName的QSqlDatabase
比如线程A
QSqlDatabase::addDatabase("QSQLITE", "A");
QSqlQuery query(QSqlDatabase::database("A"));
线程B
QSqlDatabase::addDatabase("QSQLITE", "B");
QSqlQuery query(QSqlDatabase::database("B"));

2)实现一个数据库线程池,创建N个不同connectionName的QSqlDatabase,所有的query命令都放到这个线程池中处理。

 

在此感谢网络大牛

参考地址:

http://blog.csdn.net/goldenhawking/article/details/10811409

http://blog.chinaunix.net/uid-20680966-id-4779621.html


另外两个封装

http://blog.csdn.net/wsj18808050/article/details/44891715

http://blog.csdn.net/lwwl12/article/details/76124210


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值