QCoreApplication::processEvents用法及耗时操作导致界面冻结卡死问题解决

目录

1. 函数说明

2. 需求应用场景举例

2.1. 场景1

2.2. 场景2


1. 函数说明

processEvents函数如下:

[static] void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents)
[static] void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, int ms)

可以看到有两个重载函数,且都是QCoreApplication静态成员函数,第二个函数多了超时时间参数,基本同第一函数功能类似。其作用是:

  • 如果正在处理事件的时间超过了指定的时间限制 ms,processEvents 函数仍然会等待事件处理完成,然后返回。
  • 如果事件处理完成前的时间限制 ms 达到了,那么 processEvents 函数将立即返回,即使事件队列中仍有待处理的事件。
  • 为调用线程即调用本函数所在的线程处理所有阻塞事件,具体处理哪些类型的阻塞事件由本函数的参数flags决定。当在程序的某个耗时操作偶尔调用一下本函数,可以解决界面冻结、卡死、显示不出来的问题(见后面的例子)。

       本函数的QEventLoop::ProcessEventsFlags参数是枚举类型,各枚举值说明如下: 

枚举值具体值作用及含义

AllEvents

0x00

所有事件,但DeferredDelete被特殊处理,具体参见Qt Assist中的QObject::deleteLater()的说明

ExcludeUserInputEvents

0x01

不处理鼠标、键盘事件。注意:鼠标、键盘事件不会被丢弃,它只是不处理而已,等下次调用processEvents函数且参数不是ExcludeUserInputEvents时才处理鼠标、键盘事件

ExcludeSocketNotifiers

0x02

不处理套接字通知事件。注意:套接字通知事件不会被丢弃,它只是不处理而已,等下次调用processEvents函数且参数不是ExcludeSocketNotifiers时才处理鼠标、键盘事件

WaitForMoreEvents

0x04

如果没有阻塞事件,则一直等待

可以通过位或操作(|),将上述参数联合起来用,如: 

QEventLoop::ProcessEventsFlags(QEventLoop::AllEvents | QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);

上述代码的意思是:除了不处理键盘、鼠标、套接字通知事件外,其它事件都处理。 

2. 需求应用场景举例

2.1. 场景1

如下代码:

CQueryDataProgressDlg exportProgressDlg(this); // 进度提示框窗体
exportProgressDlg.show();
exportProgressDlg.setRange(1,100); // 进度条范围为[1, 100]
exportProgressDlg.setTip(QstringLiteral("正在导出数据,请稍候..."));
for (auto pageIndex = 0, exportProgress = 1; pageIndex < 100; ++pageIndex, ++exportProgress) // 数据共100页
{
    //QCoreApplication::processEvents();
    //QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);

    // 按页导出数据库中的数据
    exportDataToLocal(pageIndex);  // 此句代码非常耗时

    // 每导出1页数据,进度条就向前推进一步
    exportProgressDlg.setPos(exportProgress);
}

     读者不必纠结上述代码的完整实现,可以将上述代码理解为伪代码,CQueryDataProgressDlg 可以理解为一个进度提示框窗体,上面的代码是按页索引(pageIndex)导出数据库中的数据到本地。其中exportDataToLocal函数负责按页索引查询数据库并将查询结果保存到本地,其非常耗时,每导出一页数据,CQueryDataProgressDlg窗体表示的进度条就前进一格,这种边执行边告知用户执行进度的需求,在现实中很常见,然而,如果注释掉或不加第7行代码,则进度条窗体压根不会显示出来,因为导出数据很耗时导致Qt的事件循环被阻塞,进度提示框窗体事件得不到执行而不显示、进度条不会更新。加入第7行代码后,进度条窗体显示如下且进度条能按预定设想的那样向前走动,且按住鼠标左键拖动窗体时,窗体能被拖动;可以尝试在该窗体加入一个编辑框,此时编辑框能接受键盘输入。

如果将第7行换成第8行,即processEvents函数的参数改为QEventLoop::ExcludeUserInputEvents,则按住鼠标左键拖动窗体时,窗体不能被拖动,即鼠标事件被屏蔽了,可以尝试在该窗体加入一个编辑框,此时编辑框不接受任何键盘输入。

2.2. 场景2

线程中有死循环,导致没有机会处理事件,执行槽函数。 

// Work类的槽函数:

// 先触发这个槽函数,里面有while循环,会卡住线程
void Worker::doWork() 
{
    qDebug() << __FUNCTION__ << " Thread ID: " << QThread::currentThreadId() << "\n";

    // 循环10次,每次1s
    int i = 1;
    while (i < 10)
    {
        i++;
        Sleep(1000);
        qDebug() << "second : " << i << " s\n";

        //QCoreApplication::processEvents();    // 
    }
}

// 再触发这个槽函数,会因为线程在做while循环,没有机会得到执行
void Worker::doWork2() 
{
    qDebug() << __FUNCTION__ << " Thread ID: " << QThread::currentThreadId();
}

----------------------------------------------------------------------------------------------------------

// Main函数中的代码
auto *worker = new Worker;
_workerThread = new QThread(this);
worker->moveToThread(_workerThread);    // worker的槽函数都在_workerThread线程中执行

connect(this, SIGNAL(operate()), worker, SLOT(doWork(int)));
connect(this, SIGNAL(operate2()), worker, SLOT(doWork2(int)));
    
// 启动线程
_workerThread->start();

emit operate();    // 先让线程中的while循环跑起来
emit operate2();   // 再触发doWork2槽函数,但是因为子线程中doWork在while循环,
                   // 此事件没有机会被处理,直至while循环结束才得到doWork2执行的机会。
结果1:
注掉QCoreApplication::processEvents(); 阻塞情况:

Worker::doWork  Thread ID:  0x4260 

second :  2  s
second :  3  s
second :  4  s
second :  5  s
second :  6  s
second :  7  s
second :  8  s
second :  9  s

Worker::doWork2  Thread ID:  0x4260
结果2:
放开注释 QCoreApplication::processEvents(); 解决阻塞问题后:

Worker::doWork  Thread ID:  0x4260 

second :  2  s

Worker::doWork2  Thread ID:  0x4260        // 执行processEvents时,如果有事件被阻塞了,立刻执行其槽函数

second :  3  s
second :  4  s
second :  5  s
second :  6  s
second :  7  s
second :  8  s
second :  9  s

场景2的代码摘自:Qt processEvents - 解决线程中事件阻塞(如槽函数被阻塞)

void text_reboot_static::reboot(const QString &i) { ui->test_record->append("开始第" + i + "次重启"); const int TIMEOUT_SEC = 360; // 设置3分钟超时 QDateTime startTime = QDateTime::currentDateTime(); QDateTime fixedTime; bool gga_fixed = false; bool ins_fixed = false; // 改造后的主循环结构 while (true) { // 1. 超时判定(正确计算已用时间) qint64 elapsed_sec = startTime.secsTo(QDateTime::currentDateTime()); if (elapsed_sec >= TIMEOUT_SEC) { ui->test_record->append("超时未完成固定,耗时3分钟"); return; } // 2. 数据池空检测与等待 if (datapool::instance()->isEmpty()) { QThread::msleep(100); // 无数据时等待100ms QCoreApplication::processEvents(); // 保持UI响应 continue; // 跳过后续处理直接进入下一轮循环 } // 3. 取出并处理数据 QString m_data = datapool::instance()->takeData(); int check_result = paraser->check_data(m_data); // 4. 解析结果处理(逻辑优化) switch (check_result) { case 0: ui->test_record->append("数据解析失败"); break; case 1: if (paraser->GGA.quality == 4 && !gga_fixed) { ui->test_record->append("[GGA已固定]"); gga_fixed = true; } break; case 3: if (paraser->INS.INS_STATUS == "INS_SOLUTION_GOOD" && paraser->INS.POS_STATUS == "INS_RTKFIXED" && !ins_fixed) { ui->test_record->append("[INS已固定]"); ins_fixed = true; } break; } // 5. 双条件完成检测 if (gga_fixed && ins_fixed) { fixedTime = QDateTime::currentDateTime(); qint64 elapsed = startTime.msecsTo(fixedTime); ui->test_record->append("检测完毕!"); ui->test_record->append("启动时间: " + startTime.toString("hh:mm:ss.zzz")); ui->test_record->append("固定时间: " + fixedTime.toString("hh:mm:ss.zzz")); ui->test_record->append(QString("耗时: %1 毫秒").arg(elapsed)); return; } // 6. 事件循环处理与节奏控制 QCoreApplication::processEvents(); QThread::msleep(100); // 保持1Hz检测频率 } } void text_reboot_static::long_time_static(const QString &time) { // 参数有效性验证 bool conversionOk; int hours = time.toInt(&conversionOk); if (!conversionOk || hours <= 0) { qWarning() << "无效的时间参数:" << time; return; } // 初始化计时器 const auto timeout = std::chrono::hours(hours); const auto startTime = std::chrono::steady_clock::now(); // 主循环逻辑 while (true) { const auto currentTime = std::chrono::steady_clock::now(); const auto elapsed = currentTime - startTime; // 计算进度百分比(50-100) // double progress = 50.0 + (50.0 * (elapsed.count() / static_cast<double>(timeout.count()))); // ui->progressBar->setValue(static_cast<int>(progress)); // this->take_data(); qInfo()<< QDateTime::currentDateTime(); // 超时判断 if (elapsed >= timeout) { ui->test_record->append("长时间静态检测完毕,请查看日志结果"); // ui->progressBar->setValue(100); // 确保到达100% break; } // 6. 事件循环处理与节奏控制 QCoreApplication::processEvents(); QThread::msleep(100); // 保持1Hz检测频率 } }第二个函数老感觉有问题
03-13
ui->test_record->append("开始第"+ i+"次重启"); // 判定数据库是否存在数据如果有则取出数据如果没有则等待数据进入 if(datapool::instance()->isEmpty()) { QThread::msleep(1000); //停留1秒若是还没有数据则返回无数据 if(datapool::instance()->isEmpty()) { ui->test_record->append("机器未接收到有效数据或未开机"); return ; } } const int TIMEOUT_SEC = 180; // 设置3分钟超时 QDateTime startTime = QDateTime::currentDateTime(); QDateTime fixedTime; bool gga_fixed = false; bool ins_fixed = false; // 改造后的循环条件 while(QDateTime::currentDateTime().secsTo(startTime) < TIMEOUT_SEC) { QString m_data = datapool::instance()->takeData(); int check_result = paraser->check_data(m_data); if(check_result == 0) { ui->test_record->append("数据解析失败"); } else if(check_result == 1) { if(paraser->GGA.quality == 4) { if(!gga_fixed) { ui->test_record->append("[GGA已固定]"); gga_fixed = true; } } } else if(check_result == 3) { if(paraser->INS.INS_STATUS == "INS_SOLUTION_GOOD" && paraser->INS.POS_STATUS == "INS_RTKFIXED") { if(!ins_fixed) { ui->test_record->append("[INS已固定]"); ins_fixed = true; } } } // 双条件满足检测 if(gga_fixed && ins_fixed) { fixedTime = QDateTime::currentDateTime(); qint64 elapsed = startTime.msecsTo(fixedTime); ui->test_record->append("检测完毕!"); ui->test_record->append("启动时间: " + startTime.toString("hh:mm:ss.zzz")); ui->test_record->append("固定时间: " + fixedTime.toString("hh:mm:ss.zzz")); ui->test_record->append(QString("耗时: %1 毫秒").arg(elapsed)); return; // 直接退出方法 } QCoreApplication::processEvents(); // 处理事件循环 QThread::msleep(100); // 一般来说数据输出都是1hz所以沉睡0.1s防止数据没进来 }这一段代码中的从数据池中取数据也改成如果数据池中没数据就等待如果数据池中有数据就取出运算无论数据池子有没有数据三分钟没固定直接结束这一次计算输出调试信息
03-08
<think>好的,我现在需要帮助用户修改他们的代码。用户的代码目前是从数据池中取出数据进行处理,但用户希望调整这部分逻辑,使得当数据池中没有数据时,程序等待,直到有数据为止。同时,如果三分钟内没有完成固定,就结束这次计算并输出调试信息。 首先,我需要理解用户提供的现有代码的结构。当前代码中有一个while循环,循环条件是当前时间与开始时间的差值小于超时时间(180秒)。在循环内部,每次都会从数据池中取出数据,然后根据解析结果更新GGA和INS的固定状态。如果两者都固定,就记录时间并退出。否则,每隔100毫秒检查一次,同时处理事件循环。 用户的需求是修改从数据池中取数据的部分,改为当数据池没有数据时等待,有数据时才取出处理。同时,无论数据池是否有数据,如果三分钟内没有完成固定,就结束这次计算。 首先,原来的代码使用datapool::instance()->takeData()来取出数据。如果数据池为空,takeData()可能会返回空或者无效数据?需要确认takeData()的行为。但根据用户当前的代码,在进入循环前已经检查了数据池是否为空,并且等待了1秒,如果仍然为空则返回。不过用户可能希望将等待的逻辑移到循环内部,以便在每次循环中检查数据池是否有数据,没有的话就等待,而不是直接取出。 原来的代码在循环中直接取出数据,无论数据池是否为空。这可能存在问题,因为如果数据池为空,takeData()可能返回空字符串,导致解析失败。用户希望改为只有在有数据时才取出处理,否则等待。 因此,修改后的逻辑应该是:在每次循环中,首先检查数据池是否为空。如果为空,等待一段时间(比如100毫秒),然后继续循环。如果有数据,则取出数据进行处理。同时,保持超时机制,即总时间超过3分钟则退出。 需要调整循环内的结构,添加对数据池是否为空的检查。如果数据池为空,则等待,但不增加已用时间吗?或者等待的时间应计入超时时间?根据用户的需求,无论数据池是否有数据,三分钟未固定就结束。因此,超时是从开始时间计算的,不管中间是否等待数据,只要总时间超过3分钟就退出。 接下来,如何实现“如果数据池中没有数据就等待,有数据再取出”。在循环内部,每次迭代时,首先检查数据池是否为空。如果为空,则睡眠一段时间(比如100ms),然后继续循环。否则,取出数据并处理。 但这样可能会导致在数据池为空时,频繁地检查并睡眠,从而占用CPU。所以需要使用一种更高效的等待方式,例如等待直到有数据可用,或者使用信号量机制。但根据用户提供的代码,可能数据池是使用Qt框架中的某种线程安全队列,或者类似的结构,可能没有内置的等待机制。因此,在代码中可能需要轮询数据池的状态。 原来的代码在进入循环之前检查了数据池是否为空,并等待了1秒。用户可能希望将这个等待逻辑移到循环内部,即在每次循环时检查数据池是否有数据,如果没有则等待,直到有数据为止,但不超过总超时时间。 但需要考虑,如果在循环中等待数据的时间过长,可能会导致超时时间被超过。例如,如果数据池一直为空,循环会不断等待,但每次循环迭代都会检查是否超时。因此,在每次循环开始,先检查是否超时,如果已经超时,则退出循环。否则,检查数据池是否有数据,如果没有,则等待一定时间(比如100ms),然后继续循环。如果有数据,则取出处理。 因此,修改后的代码结构可能是: 在while循环内: 1. 检查是否已经超时,如果是,则跳出循环,输出超时信息。 2. 检查数据池是否为空: a. 如果为空,等待100ms,然后继续循环。 b. 如果不为空,取出数据进行处理。 3. 处理数据,更新GGA和INS状态。 4. 检查是否两者都已固定,如果是,记录时间并退出。 5. 处理事件循环,避免界面冻结。 但需要注意,原来的循环条件是QDateTime::currentDateTime().secsTo(startTime) < TIMEOUT_SEC,这可能有问题,因为secsTo返回的是当前时间到startTime的秒数差,如果是未来的时间,secsTo会返回负数。所以正确的计算已用时间应该是currentDateTime() - startTime,即经过的时间是否超过TIMEOUT_SEC。 例如,原代码中的循环条件可能应该改为: while (startTime.secsTo(QDateTime::currentDateTime()) < TIMEOUT_SEC) 或者更准确的方式是计算已用时间是否小于超时时间: while (true) { qint64 elapsed_sec = startTime.secsTo(QDateTime::currentDateTime()); if (elapsed_sec >= TIMEOUT_SEC) { break; } // 其他处理 } 但原来的写法是QDateTime::currentDateTime().secsTo(startTime),这实际上计算的是当前时间到startTime的秒数差,如果startTime是过去的时间,那么结果会是负数。例如,如果现在比startTime晚10秒,那么currentDateTime().secsTo(startTime)会返回-10。因此,原循环条件中的表达式QDateTime::currentDateTime().secsTo(startTime) < TIMEOUT_SEC实际上是判断当前时间是否在startTime的TIMEOUT_SEC秒之前。这显然有问题,因为随着时间推移,这个值会越来越小(负数绝对值越来越大),所以循环条件可能在开始时就不满足,导致循环无法执行。这可能是一个错误,正确的应该是计算已用时间是否小于TIMEOUT_SEC。 例如,正确的应该是: qint64 elapsed_seconds = startTime.secsTo(QDateTime::currentDateTime()); if (elapsed_seconds >= TIMEOUT_SEC) { break; } 因此,用户的原代码可能存在逻辑错误,导致循环条件不正确。需要指出这一点,或者在修改代码时修正这个问题。 现在,回到用户的需求:无论数据池是否有数据,三分钟超时则结束。因此,在修改后的代码中,循环应该持续直到达到超时时间,或者两个条件都满足。 修改后的步骤: 1. 进入循环前记录开始时间。 2. 在循环中,首先检查是否已经超时,如果是,输出超时信息并返回。 3. 检查数据池是否为空: a. 如果为空,等待一定时间(如100ms),然后继续循环。 b. 如果不为空,取出数据并处理。 4. 处理数据,更新GGA和INS状态。 5. 如果两者都固定,记录时间并返回成功。 6. 处理事件循环,避免界面无响应。 7. 每次循环后,可能需要再次检查超时。 但需要考虑,如果数据池长时间没有数据,程序会一直等待,直到超时。例如,每次循环时,如果数据池为空,就等待100ms,然后继续循环,直到超时。 但原来的代码中,在取出数据前没有检查数据池是否为空,直接调用takeData(),这可能假设数据池中有数据。但用户希望改为只有在有数据时才取出,否则等待。 因此,修改后的代码结构大致如下: 在while循环中: - 检查是否超时,如果是,跳出循环。 - 检查数据池是否为空: - 如果为空,等待100ms,然后continue,进入下一次循环。 - 如果不为空,取出数据并处理。 - 处理数据,更新状态。 - 检查是否满足条件,如果满足则退出。 - 处理事件循环。 但需要考虑,如果在数据池为空时,等待的时间是否应该计入超时时间。例如,当数据池一直为空,程序会不断等待100ms,直到总时间超过3分钟,然后退出。 因此,修改后的代码大致如下: 将原来的循环条件改为无限循环,然后在循环内部检查是否超时,以及数据池的状态。 具体代码修改步骤: 1. 将原来的while循环条件改为while(true),然后在循环内部检查是否超时。 2. 在循环开始时,计算已用时间,如果超过TIMEOUT_SEC,则输出超时信息并返回。 3. 检查数据池是否为空: a. 如果为空,等待100ms,处理事件循环,然后继续。 b. 如果不为空,取出数据并处理。 例如: // 改造后的循环条件 while (true) { // 检查是否超时 qint64 elapsed_sec = startTime.secsTo(QDateTime::currentDateTime()); if (elapsed_sec >= TIMEOUT_SEC) { ui->test_record->append("超时未完成固定"); return; } // 检查数据池是否为空 if (datapool::instance()->isEmpty()) { QThread::msleep(100); // 等待100ms QCoreApplication::processEvents(); // 处理事件 continue; // 继续下一次循环 } // 取出数据 QString m_data = datapool::instance()->takeData(); // 处理数据 int check_result = paraser->check_data(m_data); // ... 其余处理逻辑 // 检查是否满足条件 if (gga_fixed && ins_fixed) { // 记录时间并退出 return; } } 但需要注意,原来的代码中在循环末尾有QThread::msleep(100);,但修改后的逻辑中,当数据池不为空时,取出数据处理后,可能不需要睡眠,而是立即继续处理下一个数据。但数据池可能一次取出一个数据,处理完后需要继续检查。 不过,如果数据池中有多个数据,可能需要处理完所有数据,但用户可能希望每次循环处理一个数据,然后继续检查超时和条件。 此外,原来的代码中的QCoreApplication::processEvents(); 是为了保持UI响应,必须保留。 现在,用户是否希望当数据池有数据时立即处理,而不需要等待100ms?例如,如果有数据,就处理;如果没有,等待100ms再检查。这样修改后的代码结构应该可以满足。 另外,原来的代码在进入循环前有一个检查数据池是否为空的逻辑,如果数据池为空,等待1秒,如果仍然为空,输出未接收数据的信息并返回。用户是否希望移除这个前置检查,而完全依赖循环内的检查?根据用户的需求描述,他们希望无论数据池是否有数据,只要在三分钟内没有完成固定就结束。因此,前置检查可能需要移除,或者保留但调整。 例如,原代码中的前置检查: if(datapool::instance()->isEmpty()) { QThread::msleep(1000); if(datapool::instance()->isEmpty()) { ui->test_record->append("机器未接收到有效数据或未开机"); return ; } } 这段代码的作用是在进入循环前检查数据池是否为空,如果为空等待1秒,如果还是空,就认为机器未开机或未接收数据,直接返回。但根据用户的新需求,可能希望即使数据池在开始时为空,也要等待最多三分钟,直到数据到来。因此,这段前置检查可能需要被移除,而将数据池是否为空的检查移到循环内部。 因此,在修改后的代码中,应该删除原来的前置检查,改为在循环内部处理数据池为空的情况,直到超时为止。 总结修改步骤: 1. 删除原来的前置检查数据池是否为空的代码(即删除if(datapool::instance()->isEmpty())的那段)。 2. 在循环内部,首先检查是否超时,如果是则输出超时信息并返回。 3. 检查数据池是否为空,如果为空,等待100ms,处理事件循环,然后继续下一次循环。 4. 如果数据池不为空,取出数据并处理。 5. 处理数据后检查GGA和INS是否固定,如果是则记录时间并返回。 6. 每次循环处理事件循环,并适当睡眠以避免CPU过载。 因此,修改后的代码大致如下: // 设置3分钟超时 const int TIMEOUT_SEC = 180; QDateTime startTime = QDateTime::currentDateTime(); QDateTime fixedTime; bool gga_fixed = false; bool ins_fixed = false; while (true) { // 检查是否超时 qint64 elapsed_sec = startTime.secsTo(QDateTime::currentDateTime()); if (elapsed_sec >= TIMEOUT_SEC) { ui->test_record->append("超时未完成固定,耗时3分钟"); return; } // 检查数据池是否有数据 if (datapool::instance()->isEmpty()) { // 等待100ms并处理事件 QThread::msleep(100); QCoreApplication::processEvents(); continue; } // 取出数据 QString m_data = datapool::instance()->takeData(); // 处理数据 int check_result = paraser->check_data(m_data); if (check_result == 0) { ui->test_record->append("数据解析失败"); } else if (check_result == 1) { if (paraser->GGA.quality == 4) { if (!gga_fixed) { ui->test_record->append("[GGA已固定]"); gga_fixed = true; } } } else if (check_result == 3) { if (paraser->INS.INS_STATUS == "INS_SOLUTION_GOOD" && paraser->INS.POS_STATUS == "INS_RTKFIXED") { if (!ins_fixed) { ui->test_record->append("[INS已固定]"); ins_fixed = true; } } } // 检查是否双固定 if (gga_fixed && ins_fixed) { fixedTime = QDateTime::currentDateTime(); qint64 elapsed = startTime.msecsTo(fixedTime); ui->test_record->append("检测完毕!"); ui->test_record->append("启动时间: " + startTime.toString("hh:mm:ss.zzz")); ui->test_record->append("固定时间: " + fixedTime.toString("hh:mm:ss.zzz")); ui->test_record->append(QString("耗时: %1 毫秒").arg(elapsed)); return; } // 处理事件循环 QCoreApplication::processEvents(); } 但需要注意,原来的代码中,在每次循环末尾有QThread::msleep(100);,这里在数据池不为空的情况下,取出数据并处理后,可能不需要睡眠,以尽快处理下一个数据。但如果在取出数据后立即处理,可能会导致循环过快,消耗CPU。因此,可能需要在每次循环末尾都睡眠100ms,或者在数据池为空时睡眠,否则不睡眠。或者,根据数据到来的频率调整等待时间。 根据用户注释中的说明,“一般来说数据输出都是1hz所以沉睡0.1s防止数据没进来”,即数据每秒更新一次,因此每次处理完一个数据后,等待100ms是合理的。因此,可能需要在每次循环末尾都睡眠100ms,无论是否取到数据。但原来的代码在取出数据后也会睡眠100ms。 因此,修改后的代码可能需要调整睡眠的位置,例如: 在循环末尾添加QThread::msleep(100);,但这样在数据池不为空时,处理数据后睡眠100ms,可能降低处理速度。但根据数据是1hz的,即每秒一次,这样可能合理。 或者,当数据池不为空时,处理完数据后立即继续循环,不睡眠,以处理可能堆积的数据。但需要注意,如果数据池中有大量数据积压,可能会一次性处理完所有数据,导致循环快速执行多次,而中间没有足够的时间处理事件循环,可能导致界面卡顿。 因此,可能需要保留原来的QThread::msleep(100);在循环末尾,无论是否取到数据,以确保每次循环之间有100ms的间隔,减少CPU占用。 因此,修改后的循环结构可能如下: while (true) { // 检查超时 qint64 elapsed_sec = startTime.secsTo(QDateTime::currentDateTime()); if (elapsed_sec >= TIMEOUT_SEC) { ui->test_record->append("超时未完成固定,耗时3分钟"); return; } if (datapool::instance()->isEmpty()) { // 数据池为空,等待100ms QThread::msleep(100); QCoreApplication::processEvents(); continue; } // 取出数据并处理 QString m_data = datapool::instance()->takeData(); // ... 处理数据逻辑 // 检查是否双固定 if (gga_fixed && ins_fixed) { // ... 输出信息并返回 } QCoreApplication::processEvents(); QThread::msleep(100); // 保持原来的睡眠 } 但这样,当数据池不为空时,取出数据并处理后,仍然会睡眠100ms。这可能符合原来的设计,避免过快循环。 不过,用户可能希望当数据池有数据时立即处理,而不需要等待100ms。例如,如果有数据,立即处理,没有数据时等待。但根据数据是1hz的,处理完后等待100ms可能不会有太大影响。 综合以上分析,修改后的代码应该: - 移除原来的前置数据池检查。 - 在循环内部首先检查超时。 - 检查数据池是否为空,为空则等待100ms后继续。 - 不为空则取出数据处理。 - 处理完成后,检查是否满足条件。 - 最后处理事件循环并睡眠100ms。 同时,需要修正原来的超时判断逻辑,使用正确的已用时间计算。 现在,关于时间计算的问题,原代码中的循环条件QDateTime::currentDateTime().secsTo(startTime) < TIMEOUT_SEC是错误的,因为secsTo的参数是未来的时间才会返回正值。正确的做法是计算当前时间与startTime的时间差,例如: qint64 elapsed_seconds = startTime.secsTo(QDateTime::currentDateTime()); 如果startTime是过去的时间,elapsed_seconds会是正数,表示已经过去了多少秒。因此,超时的条件应该是elapsed_seconds >= TIMEOUT_SEC。 因此,修改后的代码中,超时判断应该使用这个正确的条件。 最后,用户希望无论数据池是否有数据,三分钟未固定则结束。因此,在超时的情况下,无论数据池是否为空,都会退出循环,并输出调试信息。 综上所述,修改后的代码应该类似于: // 设置3分钟超时 const int TIMEOUT_SEC = 180; QDateTime startTime = QDateTime::currentDateTime(); QDateTime fixedTime; bool gga_fixed = false; bool ins_fixed = false; while (true) { // 计算已过时间 qint64 elapsed_sec = startTime.secsTo(QDateTime::currentDateTime()); if (elapsed_sec >= TIMEOUT_SEC) { ui->test_record->append("超时未完成固定,耗时3分钟"); return; } // 检查数据池是否为空 if (datapool::instance()->isEmpty()) { QThread::msleep(100); QCoreApplication::processEvents(); continue; } // 取出数据 QString m_data = datapool::instance()->takeData(); int check_result = paraser->check_data(m_data); if (check_result == 0) { ui->test_record->append("数据解析失败"); } else if (check_result == 1) { if (paraser->GGA.quality == 4 && !gga_fixed) { ui->test_record->append("[GGA已固定]"); gga_fixed = true; } } else if (check_result == 3) { if (paraser->INS.INS_STATUS == "INS_SOLUTION_GOOD" && paraser->INS.POS_STATUS == "INS_RTKFIXED" && !ins_fixed) { ui->test_record->append("[INS已固定]"); ins_fixed = true; } } // 检查双固定 if (gga_fixed && ins_fixed) { fixedTime = QDateTime::currentDateTime(); qint64 elapsed = startTime.msecsTo(fixedTime); ui->test_record->append("检测完毕!"); ui->test_record->append("启动时间: " + startTime.toString("hh:mm:ss.zzz")); ui->test_record->append("固定时间: " + fixedTime.toString("hh:mm:ss.zzz")); ui->test_record->append(QString("耗时: %1 毫秒").arg(elapsed)); return; } QCoreApplication::processEvents(); QThread::msleep(100); // 防止CPU过高 } 这样修改后,代码会持续检查数据池,当有数据时立即处理,没有数据时等待100ms。同时,总运行时间超过3分钟则超时退出。 最后,需要验证修改后的代码是否符合用户需求: 1. 当数据池有数据时,立即取出处理。 2. 当数据池为空时,等待100ms再检查,不增加额外等待时间。 3. 无论数据池是否有数据,三分钟超时则结束。 4. 处理过程中保持UI响应。 这样应该满足用户的要求。</think>以下是针对代码的修改步骤和解释: ### 修改目标 1. **数据池无数据时等待**:循环中若数据池为空,等待100ms后继续检查 2. **强制超时机制**:无论数据是否持续接收,3分钟未完成固定直接退出 3. **移除冗余检查**:删除进入循环前的前置等待,将超时判断整合到主循环 ### 代码修改方案 ```cpp ui->test_record->append("开始第" + i + "次重启"); const int TIMEOUT_SEC = 180; // 设置3分钟超时 QDateTime startTime = QDateTime::currentDateTime(); QDateTime fixedTime; bool gga_fixed = false; bool ins_fixed = false; // 改造后的主循环结构 while (true) { // 1. 超时判定(正确计算已用时间) qint64 elapsed_sec = startTime.secsTo(QDateTime::currentDateTime()); if (elapsed_sec >= TIMEOUT_SEC) { ui->test_record->append("超时未完成固定,耗时3分钟"); return; } // 2. 数据池空检测与等待 if (datapool::instance()->isEmpty()) { QThread::msleep(100); // 无数据时等待100ms QCoreApplication::processEvents(); // 保持UI响应 continue; // 跳过后续处理直接进入下一轮循环 } // 3. 取出并处理数据 QString m_data = datapool::instance()->takeData(); int check_result = paraser->check_data(m_data); // 4. 解析结果处理(逻辑优化) switch (check_result) { case 0: ui->test_record->append("数据解析失败"); break; case 1: if (paraser->GGA.quality == 4 && !gga_fixed) { ui->test_record->append("[GGA已固定]"); gga_fixed = true; } break; case 3: if (paraser->INS.INS_STATUS == "INS_SOLUTION_GOOD" && paraser->INS.POS_STATUS == "INS_RTKFIXED" && !ins_fixed) { ui->test_record->append("[INS已固定]"); ins_fixed = true; } break; } // 5. 双条件完成检测 if (gga_fixed && ins_fixed) { fixedTime = QDateTime::currentDateTime(); qint64 elapsed = startTime.msecsTo(fixedTime); ui->test_record->append("检测完毕!"); ui->test_record->append("启动时间: " + startTime.toString("hh:mm:ss.zzz")); ui->test_record->append("固定时间: " + fixedTime.toString("hh:mm:ss.zzz")); ui->test_record->append(QString("耗时: %1 毫秒").arg(elapsed)); return; } // 6. 事件循环处理与节奏控制 QCoreApplication::processEvents(); QThread::msleep(100); // 保持1Hz检测频率 } ``` ### 关键改进说明(Markdown公式展示) 1. **超时计算修正** 原代码存在时间差计算方向错误,正确公式应为: $$ \text{elapsed\_sec} = \text{currentTime} - \text{startTime} $$ 使用`startTime.secsTo(QDateTime::currentDateTime())`确保得到正值时间差 2. **数据等待机制** 通过`continue`实现空数据池时的等待: $$ \text{空数据池} \Rightarrow \text{sleep}(100ms) \Rightarrow \text{重试} $$ 避免无数据时持续空转消耗CPU 3. **频率控制** 保持总体检测频率约10Hz(通过末尾的`QThread::msleep(100)`),符合硬件1Hz数据输出的特性,公式表达: $$ \text{检测周期} = 100ms \Rightarrow \text{采样率} = \frac{1}{0.1} = 10Hz $$ ### 效果验证 - **正常流程**:收到数据后立即处理,双条件满足时正常退出 - **无数据场景**:持续等待直至3分钟超时,输出提示信息 - **断断续续数据**:有数据时立即处理,无数据时等待,总时间不超过3分钟 建议在实际环境中测试超时触发和数据中断场景,观察日志输出是否符合预期。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值