参考博客:QThread必须要了解的几个函数
https://blog.csdn.net/t46414704152abc/article/details/52155777
设计思路:读文件生成sql语句写入内存为一个线程,返回sql语句给主线程;主线程再调用写数据库操作线程。
其中包括:文件的读取;qt多线程;多线程访问数据库写入数据。
使用线程池读取大数据文件,测试通过。
测试使用的三个文件每个文件有32万多条数据,总计94万条数据,全部写入数据库预计需要三分钟时间。
整体结构:
ThreadPractice.h
// 使用线程池读取大数据文件,测试通过。
// 测试使用的三个文件每个文件有32万多条数据,总计94万条数据,全部写入数据库预计需要三分钟时间。
#pragma once
#pragma execution_character_set("utf-8")
#include
#include
#include
#include
#include
#include
#include
#include
#include
// 保存数据库路径
static QString save_db_path;
// 读取文件线程 注册线程中处理函数,线程结束给主线程返回信号同时启动写数据库线程
class FileRunnable
: public QObject
, public QRunnable
{
Q_OBJECT
public:
explicit FileRunnable(QObject * parent = nullptr) {}
~FileRunnable() {}
void SetFunction(QStringList(FileRunnable::*_pf)(QString, QString)) {
FileRunnable::pf = _pf;
}
protected:
void run() {
QStringList sql_list = ThreadReadRangeFile(path_, table_name_);
emit SendRunableSignal(sql_list);
return;
}
signals:
void SendRunableSignal(QStringList);
public:
QStringList ThreadReadRangeFile(QString, QString);
QStringList(FileRunnable::*pf)(QString _path, QString _table_name);
QString path_, table_name_;
};
// 数据库文件线程 注册线程中处理函数,线程结束给主线程返回信号
class DBRunnable
: public QObject
, public QRunnable
{
Q_OBJECT
public:
explicit DBRunnable(QObject* parent = nullptr){}
~DBRunnable(){}
void SetFuntion(bool(DBRunnable::*_pf)(QStringList)) {
DBRunnable::pf = _pf;
}
protected:
void run() {
bool flag = THreadWriteRangeTable(sql_list);
emit SendRunableSignal(flag);
return;
}
signals:
void SendRunableSignal(bool);
public:
// 写入Range文件到数据库
bool THreadWriteRangeTable(QStringList);
public:
bool(DBRunnable::*pf)(QStringList);
QStringList sql_list;
QString uid;
};
class ThreadPractice
: public QWidget
{
Q_OBJECT
public:
ThreadPractice(QWidget *parent = nullptr);
~ThreadPractice();
private:
void Init();
// 将.range文件转为db文件
void FileRangeConversion(QString _path, QString _table_name);
// 将.rcs文件转为db文件
void FileRcseConversion(QString _path, QString _table_name);
// 将.img文件转为db文件
void FileImgConversion(QString _path, QString _table_name);
private slots:
// 读取文件返回数据库语句
void ThreadReadRangeFileSlot(QStringList);
// 写入Range文件到数据库
void ThreadWriteRangeTableSlot(bool);
private:
// 线程池对象
QThreadPool *thread_pool_;
};
ThreadPractice.cpp
#include "ThreadPractice.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
QMutex mutex;
ThreadPractice::ThreadPractice(QWidget *parent)
: QWidget(parent)
{
Init();
}
ThreadPractice::~ThreadPractice()
{
}
void ThreadPractice::Init()
{
thread_pool_ = new QThreadPool();
thread_pool_->setMaxThreadCount(20); // 线程池最多20个线程
QFileInfoList InfoList = QDir(QApplication::applicationDirPath() + "/../../ThreadPractice/data/").entryInfoList(); // 获取当前目录下所有文件
foreach (QFileInfo fileInfo, InfoList)
{
if (!fileInfo.isFile()) // 不是文件继续,只用于加速
continue;
if (fileInfo.suffix() == "range") {
FileRangeConversion(fileInfo.filePath(), fileInfo.fileName());
}
if (fileInfo.suffix() == "rcs") {
FileRcseConversion(fileInfo.filePath(), fileInfo.fileName());
}
if (fileInfo.suffix() == "img") {
FileImgConversion(fileInfo.filePath(), fileInfo.fileName());
}
}
}
void ThreadPractice::FileRangeConversion(QString _path, QString _table_name)
{
// 创建读取文件线程
FileRunnable *runnable = new FileRunnable();
runnable->path_ = _path;
runnable->table_name_ = _table_name;
connect(runnable, &FileRunnable::SendRunableSignal, this, &ThreadPractice::ThreadReadRangeFileSlot);
runnable->SetFunction(&FileRunnable::ThreadReadRangeFile);
runnable->setAutoDelete(true);
thread_pool_->start(runnable);
}
void ThreadPractice::FileRcseConversion(QString _path, QString _table_name)
{
}
void ThreadPractice::FileImgConversion(QString _path, QString _table_name)
{
}
bool DBRunnable::THreadWriteRangeTable(QStringList _sql_list) {
uid = QUuid::createUuid().toString();
{
// 将range转为数据库的表保存
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", uid);
db.setDatabaseName(save_db_path);
if (!db.open()) {
QMessageBox::information(0, "unable", "can not open database", QMessageBox::Cancel);
return false;
}
QSqlQuery query(db); // 默认打开
// 这个while循环为了解决当有多个文件时会产生多个线程连接数据库,此时创建表格会失败,因此等待成功后在进行后续操作。
// 此处有个疑问:为什么不能多线程同时连接数据库?
// 连接数据库创建表格失败,说被锁住了,但是我并没有上锁。
// 2023/2/1 今天看到sqlite数据库仅支持单用户读写操作
while (1) {
if (!query.exec(_sql_list[0])) {// 0为创建表格
qDebug() << query.lastError() << "right";
QThread::msleep(1); // 如果操作失败则等待(不确定是否是query连接数据库时自动上锁)
continue;
}
break;
}
db.transaction();
for (int i = 1; i < _sql_list.size(); ++i) {
if (!query.exec(_sql_list[i]))
qDebug() << query.lastError() << "error";
}
db.commit();
query.clear();
db.close();
}
QSqlDatabase::removeDatabase(uid);
return true;
}
void ThreadPractice::ThreadWriteRangeTableSlot(bool _flag) {
if (_flag) {
qDebug() << "文件转换完成!";
}
else {
qDebug() << "文件转换失败!";
}
}
// 读去range文件
QStringList FileRunnable::ThreadReadRangeFile(QString _path, QString _table_name) {
QString save_path = QApplication::applicationDirPath() + "/../../ThreadPractice/data/";
QString table_name_ = _table_name.mid(1, 5); // 所有range文件存在一个db文件不同表格里,取文件名第20位开始10个字节的字符作为表名
save_db_path = save_path + "range.db";
// 打开文件
QFile file(_path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
qDebug() << "open filed!";
QStringList sql_list;
sql_list.clear();
// 创建表
QString create_str = QString("create table [%1] ([first] double,[second] double,[third] double,[fourth] double);").arg(table_name_);
sql_list << create_str;
// 重复表将之前数据删除
//QString delete_str = QString("delete from %1;").arg(table_name_);
//sql_list << delete_str;
QTextStream in(&file); // QTextStream读取数据
in.readLine();
while (!in.atEnd()) {
QString fileLine = in.readLine(); // 从第二行读取至下一行
QStringList list; // 用于存储文件每行信息
list.clear();
list << fileLine.split(" ", QString::SkipEmptyParts);
// 添加数据
QString insert_str = QString("insert into [%1] ([first],[second],[third],[fourth]) values ('%2','%3','%4','%5')")
.arg(table_name_).arg(list.at(0).toDouble()).arg(list.at(1).toDouble()).arg(list.at(1).toDouble()).arg(list.at(3).toDouble());
sql_list << insert_str;
}
return sql_list;
}
// 读完后发送信号并启动写数据库线程
void ThreadPractice::ThreadReadRangeFileSlot(QStringList _sql_list) {
// 执行sql语句写入数据库创建线程
DBRunnable * runnable = new DBRunnable();
QObject::connect(runnable, &DBRunnable::SendRunableSignal, this, &ThreadPractice::ThreadWriteRangeTableSlot);
runnable->sql_list = _sql_list;
runnable->SetFuntion(&DBRunnable::THreadWriteRangeTable);
runnable->setAutoDelete(true);
// 启动线程之前启动该数据库连接
thread_pool_->start(runnable);
}
main.cpp
#include
#include "ThreadPractice.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ThreadPractice w;
return a.exec();
}