1、场景一
根据需求把excel表格中的内容提取到sqlite中 ,1000条到5000条还好, 数量超过5000的时候界面就会失去响应 转圈 以为程序挂掉了 等了一段时间之后又好了 。参考博客https://blog.csdn.net/yao5hed/article/details/81108507找出了答案 。
因为读取大文件要很长时间,事件循环一直等待函数返回,这样导致阻塞事件循环。结果,GUI线程所有的绘制和交互都被阻塞在事件队列中,无法执行重绘等事件,整个程序就失去响应了。
例子:
这是常用方法(数量集过大就会发生阻塞)
QFile* file = new QFile("E:\qtgui.index");
file->open(QIODevice::ReadOnly);
QTextStream *stream = new QTextStream(file);
while(!stream->atEnd())
{
QString line = stream->readLine();
ui->textEdit->append(line);
}
解决这种阻塞问题有两种方法
1、手动强制事件循环
在任务中不断调用QCoreApplication::processEvents()手动强制事件循环,它会在处理完队列中所有事件后返回。但是如果两次函数调用的间隔时间不够短,用户仍能明显感觉到程序卡顿。所以在while循环最后加一行QApplication::processEvents();即可。 但是经测试这种办法会超级慢,如果对时间没要求的话可以考虑这种办法,否则还是放到子线程去实现把。
2**、多线程处理**。
Qt提供了三种方式:QThread、QRunnable / QThreadPool、QtConcurrent。其中最常用的是 QThread。
对于本例,使用QThread又有三种方法:信号与槽实现线程间通信、元对象系统实现线程间通信、分离线程与任务。
前两种也是跨线程调用函数的方法。使用QThread信号与槽实现线程间通信这是线程间通信比较常用的方法。代码:`
class ReadThread : public QThread
{
Q_OBJECT
public:
ReadThread(QObject* obj);
signals:
void toLine(QString line);
protected:
void run() Q_DECL_OVERRIDE;
private:
QFile* file;
QObject* m_obj;
};
ReadThread::ReadThread(QObject* obj):
m_obj(obj)
{
file = new QFile("E:\qtgui.index");
}
void ReadThread::run()
{
file->open(QIODevice::ReadOnly);
QTextStream *stream = new QTextStream(file);
while(1)
{
while(!stream->atEnd())
{
QString line = stream->readLine();
emit toLine(line);
QThread::msleep(15);
}
}
}
需要把读取任务放到run()里,构造函数要传入GUI类的指针
在GUI线程的信号与槽机制这样:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ReadThread* thread = new ReadThread(this);
thread->start();
connect(thread,SIGNAL(toLine(QString)),this,SLOT(appendText(QString)) );
connect(thread,SIGNAL(finished()),this,SLOT(FinishThread()) );
}
void MainWindow::appendText(QString lineTemp)
{
ui->textEdit->append(lineTemp);
}
使用了QThread的静态函数msleep,因为读取的文件太大,每读取一行就要更新GUI,太耗资源,会导致GUI忙不过来,读一行后稍微休息一下,否则也会阻塞GUI。
QThread的子类一般不定义槽函数,这是不安全的,可能造成主线程和子线程同时访问它,除非使用mutex保护。 但可以定义signal,而且可以在run函数中发射, 因为信号发射是线程安全的。