QT 界面阻塞相关事项

Q1 界面阻塞与资源初始化

问题描述:在启动一个界面时,需要执行一个耗时的初始化的操作,如果像下面这种写法,则会导致主线程被阻塞。

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    w.InitData();//一个耗时的操作
    return a.exec();
}

方法一、延迟执行InitData

使用

QTimer::singleShot(10, &w, SLOT(InitData()));

或者

QMetaObject::invokeMethod(&w, "InitData", Qt::QueuedConnection);

方法二、在操作过程中使用qApp->processEvents();

//InitData
for (size_t i = 0; i <= 100; i++) {
    qApp->processEvents();
    for (size_t j = 0; j < 100000000; j++) { }//delay <==> QThread::msleep(123);
}

上述方法的弊端在于,不管是使用哪种方式,InitData这个操作都是在UI线程中完成的,qApp->processEvents();可以让Qt在这个操作进行的过程中去处理事件,但是处理事件的效率受到这个delay的限制。如下图可以明显感受到进度条满了之后时间的刷新速度变快了。

考虑如下场景:

  • 界面中设置了一个label,并通过一个timer以10ms一次的间隔设置其文本为当前时间。
  • 界面中添加了一个进度条,并由InitData操作逐次修改进度值,其中InitData中的delay设置为1000ms。

经过测试,会出现如下情况:

  • InitData进行前,label每10ms刷新一次

  • InitData进行时,label刷新频率明显变慢

  • InitData结束后,label每10ms刷新一次

如何使得InitData的阻塞操作不影响UI线程,即label始终每10ms刷新一次?

方案三、使用线程来执行耗时操作

  • 将InitData操作放到线程中执行,通过信号的方式与主线程进行交互,需要注意的,这种情况下不可以在线程中初始化MainWindow的线程独占成员如socket、串口设备等。
void MainWindow::InitData(){
  for (size_t i = 0; i <= 100; i++) {
      emit setValue(i);
      QThread::msleep(1000);
  }
}
int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    std::thread t(std::bind(&MainWindow::InitData, &w));//建议使用QT推荐的QThread写法
    return a.exec();
}

当MainWindow的一部分资源初始化耗时较大时,在线程中处理对UI线程的影响较小,这种情况下,使用线程来修改MainWindow的成员是避免不了的一件事,最为稳妥的办法就是,在线程中计算出变量的最终值,然后触发信号来通知MainWindow进行变量的更新,否则就需要使用锁机制。

引申场景

异步请求

  • 考虑将请求过程放到线程空间,通过信号来控制发起请求和收到响应处理
posted @ 2023-07-24 09:09  料峭春风吹酒醒  阅读(299)  评论(0编辑  收藏  举报