概述
最近做一个多线程的远程升级软件,做完后用一个对应的测试程序进行测试,发现线程一多必崩溃,而把所有数据库处理全部删掉后,就可以同时运行几百个线程不崩溃了.原因应该是自己采用了单例模式,在各个线程中发送信号给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
最后
以上就是传统花瓣为你收集整理的多线程访问数据库问题的全部内容,希望文章能够帮你解决多线程访问数据库问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复