QTBUG-7618: QTimer consumes too much CPU

    这个Bug是在为公司一个项目编写idle延迟处理机制时发现的,在QT里面定时器类QTimer存在一个CPU高占用的问题,Bug的详细描述和解决方案已经在官方社区有提到:https://bugreports.qt-project.org/browse/QTBUG-7618

    社员Daniel Kristjansson给出一个临时解决该问题的补丁(qt4qt5),当然他也提到了最佳的解决方案:But probably the correct approach is to use nanosleep() on POSIX 1999 or later systems and simply sleep a full millisecond on systems 或者 since select is being used to also monitor other things.. pselect() could be used instead to get nanosecond accuracy。

    Daniel Kristjansson提供了示例程序来说明该Bug,我自己也写出QT和GTK+两个版本来对比,使用的CPU是Intel® Core™ i5-2400 CPU @ 3.10GHz × 4,使用的进程CPU信息查看工具是htop,操作系统用Ubuntu 12.04 LTS。

QT: 使用Version 4.8.1

#include <QtGui/QtGui>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QTimer t;
    t.start(1);

    return app.exec();
}

    qt_timer

 

GTK+:使用Version 2.0

#include<gtk/gtk.h>
#include<time.h>

void  TimePorcFunction ()
{
	//printf("ThisTimePorcFunction () \n");
    return;
}
int main(int argc,char * argv[])
{
	gtk_init(&argc,&argv);
	gtk_timeout_add(1, (GtkFunction)TimePorcFunction, NULL);
	gtk_main();
	return 1;
}

        gtk_timer

 

    对比:QTimer实现的计时器程序qt_timer在timeout间隔为1ms时,几乎100%占满CPU;而GTK+实现的gtk_timer的CPU占用率在2%-3%之间。

    在Bug报告中,Daniel Kristjansson说到“QTimer spends up to the last millisecond of the timer interval busy-waiting, in its attempt to be very accurate”。即是说QTimer内部实现时,计时器最后1ms是使用busy-waiting的方式处理,目的也就是想提高精确度。因此这个Bug的临时解决方案就是在计时器计算最后1ms时,特意加上一个数,利用除法四舍五入(rounding up),使计时器提前结束(timeout),不留下最后1ms。代码示例:

*timeout = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); 

vs

*timeout = (tv.tv_sec * 1000) + ((tv.tv_usec + 999) / 1000);

    虽然临时解决方案不完美,提前结束计时器通常意味着会提前触发某一事件,但是由于QTimer本身并不是高精度计时器,所以即使提前结束计时器的最后1ms也不会造成多大问题。

    最后看下应用Daniel Kristjansson的补丁后,上文提到的QT示例程序表现如何:

    qt_timer_plus_patch

    可以明显地看到此时QTimer实现的计时器qt_timer占用率降低,在3%-4%之间。

    总结:

    1、之所以发现这个Bug是小组老大在跟我一起分析我写的代码时,用valgrind+callgrind以及htop这个小工具分析到程序空闲时仍有计时器消耗较高的CPU,这个是分析程序的好方法;

    2、QT的Bug得先去其官方的bugreports.qt-project.org先找,像这个Bug就有Daniel Kristjansson给出这么好的解析和解决方案;

    3、有时QT的实现可以用其他库做同样的实现来对比,利于比较和发现问题;

    4、QT有社区维护,因此有问题首先到社区寻找帮助。

    最后我将此Bug有关的示例程序和patch放到我个人的github上,地址是:https://github.com/chenjiexin/qtbug7618

转载于:https://my.oschina.net/chenjiexin/blog/78245

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为了根据串口数据查询MySQL中的员工信息并展示在UI界面上,你需要在串口数据读取的地方,调用查询员工信息的函数,然后将查询结果展示在UI界面上。以下是修改后的示例代码: ```python import serial import pymysql from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel class MainWindow(QMainWindow): def __init__(self, user_id): super().__init__() self.user_id = user_id self.initUI() # 打开串口 self.ser = serial.Serial('COM1', 9600, timeout=1) def initUI(self): # 创建用于显示员工信息的控件 self.info_label = QLabel("员工信息", self) self.info_label.move(100, 50) self.info_label.setStyleSheet("font-size: 24px; color: black; background-color: #eee; border-radius: 10px;") self.id_label = QLabel("员工ID:", self) self.id_label.move(70, 100) self.id_label.setStyleSheet("font-size: 18px; color: black;") self.name_label = QLabel("姓名:", self) self.name_label.move(70, 150) self.name_label.setStyleSheet("font-size: 18px; color: black;") self.six_label = QLabel("性别:", self) self.six_label.move(70, 200) self.six_label.setStyleSheet("font-size: 18px; color: black;") self.sfz_label = QLabel("身份证:", self) self.sfz_label.move(70, 250) self.sfz_label.setStyleSheet("font-size: 18px; color: black;") self.tel_label = QLabel("电话:", self) self.tel_label.move(70, 300) self.tel_label.setStyleSheet("font-size: 18px; color: black;") # 查询员工信息 def query_employee(self, id): conn = pymysql.connect(host='39.99.214.172', user='root', password='Solotion.123', database='jj_tset') cursor = conn.cursor() cursor.execute("SELECT * FROM employee_table WHERE user_id='%s'" % id) result = cursor.fetchone() conn.close() return result # 读取数据 def read_data(self): data = self.ser.readline() if data: # 解析数据 id = data.decode().strip() # 查询员工信息 result = self.query_employee(id) if result: # 更新UI界面 self.id_label.setText("员工ID:" + result[0]) self.name_label.setText("姓名:" + str(result[1])) self.six_label.setText("性别:" + result[2]) self.sfz_label.setText("身份证:" + str(result[3])) self.tel_label.setText("电话:" + result[4]) else: # 显示空白信息 self.id_label.setText("员工ID:") self.name_label.setText("姓名:") self.six_label.setText("性别:") self.sfz_label.setText("身份证:") self.tel_label.setText("电话:") # 定时读取数据 QTimer.singleShot(100, self.read_data) def closeEvent(self, event): # 关闭串口 self.ser.close() if __name__ == '__main__': app = QApplication([]) window = MainWindow("001") window.show() window.read_data() app.exec_() ``` 在上面的代码中,我们在`MainWindow`类中定义了一个`query_employee()`函数来查询员工信息,该函数接受一个ID参数并返回一个元组,包含员工的姓名、性别、身份证和电话。然后,在`read_data()`函数中,我们读取串口数据并调用`query_employee()`函数查询员工信息,并将查询结果更新到UI界面上。 请注意,上面的代码仅供参考,你需要根据你的具体需求修改它以适应你的应用场景。例如,你可能需要将员工信息显示在不同的UI控件中,或者使用不同的布局方式来展示员工信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值