医疗器械公司的 C++/Qt 开发面试

面试问题总结

记一次医疗器械公司的 C++/Qt 开发面试

面试时间: 2026 年 11 月 4 日
面试岗位: C++/Qt 软件开发工程师
面试方向: 医疗器械、牙科影像设备
面试形式: 技术面试 + 笔试
面试结果: 等待通知

前言

最近因为一些工作上的原因,又开始重新找工作了。

简单介绍一下我的技术背景:毕业之后主要从事 C++/Qt 工业上位机开发,做过电解制氢设备的数据采集系统,涉及 Modbus TCP、多线程、PLC 通信、实时曲线、故障报警以及数据库等功能。

后来想着往底层发展,转到了嵌入式软件岗位,接触了一些新能源热管理设备的控制逻辑、CAN 和 RS-485 通信。

不过由于公司的实际情况和个人职业规划,我还是决定重新寻找 C++/Qt 相关的开发岗位。

这次面试的是一家医疗器械公司,主要做牙科影像设备。岗位比较符合我的技术背景,因此面试之前也花了不少时间准备 C++ 基础、Qt 多线程和 Modbus 通信相关知识。

原本以为面试官会主要问一些 C++ 八股文,没想到实际面试中,更多的是围绕简历项目进行深入提问。

下面记录一下这次面试中印象比较深刻的问题。


一、如果 PLC 数量增加,线程应该如何分配?

这是面试官针对我之前电解制氢上位机项目提出的问题。

之前的系统需要同时与 7 台 PLC 通信,通过 Qt 的 Modbus TCP 模块进行数据采集。

面试官问:

如果后续 PLC 数量增加到几十台甚至更多,应该如何分配线程?

事后复盘

这道题实际上是在考察多线程架构的可扩展性。

比较直接的设计是每台 PLC 使用独立的通信线程,但这种方案并不是所有场景下的最优解。

如果 PLC 数量持续增加,线程数量也会随之增长,带来额外的调度和内存开销。

考虑到 Qt 的 QModbusTcpClient 本身支持异步通信,其实可以让一个工作线程通过事件循环管理多个 PLC 连接。

每台 PLC 保持独立的客户端对象和请求状态,根据设备数量及通信负载进行分组。

如果数据解析、计算比较耗时,也可以将这些任务交给其他工作线程或线程池。

这里还有一个很重要的区别:

线程隔离、连接隔离和请求隔离并不是一回事。

例如,请求 ID 可以用于关联请求和结果,但是不能保证某台 PLC 超时后不会影响其他 PLC 的通信。

因此,我认为比较合理的设计应该同时考虑:

  1. 每台 PLC 独立的通信连接及状态管理。
  2. 合理控制每台设备的请求发送频率。
  3. 对设备进行分组,避免线程数量无限增加。
  4. 将通信和耗时的数据处理任务分离。

这道题让我意识到,实际开发中不仅要考虑功能能否实现,还要考虑设备规模增加之后,原来的架构是否依然适用。

二、设备突然断电,如何恢复数据?

第二道题考察的是数据持久化和异常恢复。

假设上位机正在采集 PLC 数据,设备或者计算机突然断电,重新启动之后应该如何恢复之前的数据?

事后复盘

这道题首先需要区分两种数据:

已经持久化的数据和仍然保存在内存中的数据。

对于已经成功提交到数据库的数据,可以在系统重新启动后通过时间戳查询,恢复历史记录。

但是对于尚未写入数据库、只存在内存中的数据,突然断电之后就可能丢失。

如果业务对数据可靠性要求比较高,可以考虑引入本地持久化队列或者预写日志机制。

例如:

  • 采集数据先写入可靠的本地持久化存储。
  • 后台线程批量写入数据库。
  • 数据库写入成功之后,更新对应的持久化状态。
  • 程序重启时,检查是否存在尚未处理的数据。

当然,这并不意味着使用了数据库就绝对不会丢数据,仍然需要考虑事务提交、刷盘策略和断电场景。

另外,历史数据恢复和设备运行状态恢复是两个不同的问题。

设备重新连接之后,应该主动读取 PLC 当前的真实状态,而不是直接沿用断电之前的运行状态。

对于无法从设备或持久化存储中恢复的数据,也需要明确标记数据缺失。

三、下发控制指令时突然断开连接怎么办?

这道题我印象比较深。

面试官提出的场景是:

上位机正在向下位机发送控制指令,这个时候连接突然断开了,应该如何处理?

我当时的回答

我的思路是在后台通信线程中建立心跳检测机制。

如果检测到设备断开连接,就先缓存尚未完成的控制指令,等待心跳检测发现设备重新连接后,再将缓存的指令下发。

当时主要考虑的是断线重连和指令不丢失。

事后复盘

后来仔细想了一下,这个回答并不完整。

最大的问题是:连接断开不一定代表指令没有执行。

例如,上位机发送一条启动指令,下位机已经收到并执行,但是在返回响应的过程中通信断开。

此时如果上位机直接缓存并重发该指令,就可能造成重复执行。

因此,比较合理的处理方式应该是:

  1. 发生通信异常时,将当前指令标记为未确认状态。
  2. 记录指令内容、发送时间和异常信息。
  3. 建立断线检测与重连机制。
  4. 连接恢复后,先查询下位机实际状态。
  5. 根据指令类型、设备状态以及是否允许重复执行,决定是否重新下发。

对于普通的参数设置指令,如果重复执行不会产生额外影响,可以根据业务规则考虑重试。

但对于启动、复位、运动控制等操作,不能简单地在重连后自动重发。

尤其是在医疗设备软件中,控制指令的安全性可能比确保指令最终发送成功更加重要。

这也是这次面试让我印象最深的一道题。

通信恢复不等于业务状态恢复,指令发送成功也不等于设备执行成功。

四、明明创建了多线程,为什么 Qt 界面还是卡顿?

这道题出现在笔试的项目场景题中。

题目大概是:程序已经创建了多个工作线程,但是某个耗时函数仍然在 UI 层执行,为什么界面还是会发生卡顿?如果由我设计,应该如何分配线程?

我的理解

Qt 的 GUI 主线程负责处理界面绘制、鼠标键盘事件以及其他事件循环任务。

即使创建了多个工作线程,只要耗时操作仍然在主线程执行,就会阻塞主事件循环。

例如:

connect(timer, &QTimer::timeout, this, [=]() {
    QThread::sleep(3);
    ui->label->setText("更新完成");
});

这里虽然使用了 QTimer,但 timeout 对应的 Lambda 仍然在主线程执行。

QThread::sleep(3) 会直接阻塞主线程,导致界面无法及时响应。

如何优化?

如果重新设计,我会根据任务类型进行划分。

UI 主线程:

负责控件更新、用户交互和界面绘制。

通信 Worker:

负责设备连接、异步请求发送、响应处理以及通信状态管理。

数据处理线程:

负责较为耗时的数据解析、计算等工作。

数据存储线程:

负责数据库写入、文件保存等可能阻塞的操作。

工作线程处理完成之后,通过 Qt 信号槽机制将结果传递给主线程。

需要注意的是,Qt 的 moveToThread() 只是修改对象的线程归属,并不会让普通成员函数调用自动切换线程。

对于跨线程任务,可以使用 QueuedConnection 将槽函数调用投递到接收对象所属线程执行。

此外,像实时曲线这样的界面操作,不能简单地全部放到工作线程中。

对于 QCustomPlot,还需要控制数据点数量、刷新频率,避免每次都重新绘制大量历史数据。

五、笔试部分

这次除了技术面试,还安排了笔试。

笔试包含选择题、算法题以及一些偏实际项目场景的问题。

项目场景题基本还是围绕 Qt 多线程、耗时操作以及系统设计展开。

这些内容因为之前做过类似的上位机项目,所以大部分能够回答出自己的思路。

不过,算法题没有写出来。

选择题也存在一些错误,面试官后来有提到。

这部分算是此次面试比较明显的短板。

之前准备面试的时候,我花了不少时间复习 C++ 基础知识,但是对实际手写算法的练习不够。

像链表反转、二分查找、STL 容器操作等题目,平时知道大概原理,但如果临时要求独立写出完整代码,还是容易卡住。

这也说明,理解某个知识点和能够在面试现场独立实现,确实是两回事。


六、关于职业方向

这次找工作还有一个比较特殊的地方。

我的上一段工作经历是嵌入式软件开发,而之前又是 Qt 上位机开发。

因此,我也提前考虑过一个问题:

为什么从上位机转到嵌入式,现在又想回到上位机?

其实原因并不复杂。

之前转嵌入式,是希望进一步了解底层设备控制和通信机制,拓宽自己的技术能力。

实际接触之后,我发现自己更希望长期围绕 C++、Qt、设备通信和工业软件开发发展。

在我看来,这两段经历也不是完全割裂的。

上位机需要与设备通信,下位机负责设备控制和数据处理。了解两边的工作机制,对开发设备控制软件还是有帮助的。

所以现在更希望找到一家研发流程相对完善、技术方向稳定的公司,继续积累 C++/Qt 相关经验。

七、这次面试带来的思考

回顾整场面试,我发现面试官其实没有特别集中地考察 C++ 八股文。

相反,问得更多的是:

  • 设备数量增加之后,原来的软件架构是否能够扩展?
  • 系统断电之后,数据能不能恢复?
  • 控制指令发送过程中断线,会不会造成重复执行?
  • 已经使用多线程了,为什么 UI 仍然可能卡顿?

这些问题都没有一个简单的 API 能够解决。

它们更像是真正开发一套设备软件时,迟早需要面对的问题。

以前做项目时,很多时候更关注功能能不能实现、设备能不能通信、数据能不能正确显示。

但这次面试让我意识到,功能实现只是第一步,后面还需要考虑软件架构、异常处理、数据可靠性和长期运行的稳定性。

另外,我目前的 C++ 基础和算法能力确实还有不少需要补充的地方。

尤其是一些平时开发中不经常主动使用的知识,过了一段时间就容易忘记。

接下来准备重点加强:

  1. C++ 对象生命周期、智能指针、移动语义和 STL。
  2. Qt 事件循环、QThread、线程间通信与线程安全。
  3. Modbus 请求调度、多设备通信与超时处理。
  4. 数据持久化、断电恢复和控制指令状态管理。
  5. 基础算法和现场编码能力。

八、最后

目前这次面试的结果还没有出来。

说实话,面试结束后还是有点忐忑。

一方面,项目方面的大部分问题基本能够回答出来;另一方面,笔试算法没写出来,部分问题的回答也不算完整。

至于最终能不能通过,就只能等待后续通知了。

不过,相比单纯拿到一个面试结果,我觉得这次经历至少让我更清楚地认识到了自己目前的技术水平。

有一定的实际项目开发经验,但距离能够独立设计一套稳定、可靠的设备软件系统,还有不少需要学习的地方。

工作一年多,感觉自己正处在一个比较尴尬的阶段。

说完全没有经验吧,确实做过一些实际项目;说技术已经很成熟吧,很多基础和系统设计问题又还没有真正吃透。

但想想也正常,技术本来就是需要长期积累的东西。

先把基础补扎实,再把项目中遇到的问题研究透彻。

至于这次面试结果如何,等有消息再更新。

希望下一份工作,能够让我安安心心地写几年代码。


记录于 2026 年 11 月 6 日。

载入内容