记一次医疗器械公司的 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 的通信。
因此,我认为比较合理的设计应该同时考虑:
- 每台 PLC 独立的通信连接及状态管理。
- 合理控制每台设备的请求发送频率。
- 对设备进行分组,避免线程数量无限增加。
- 将通信和耗时的数据处理任务分离。
这道题让我意识到,实际开发中不仅要考虑功能能否实现,还要考虑设备规模增加之后,原来的架构是否依然适用。
二、设备突然断电,如何恢复数据?
第二道题考察的是数据持久化和异常恢复。
假设上位机正在采集 PLC 数据,设备或者计算机突然断电,重新启动之后应该如何恢复之前的数据?
事后复盘
这道题首先需要区分两种数据:
已经持久化的数据和仍然保存在内存中的数据。
对于已经成功提交到数据库的数据,可以在系统重新启动后通过时间戳查询,恢复历史记录。
但是对于尚未写入数据库、只存在内存中的数据,突然断电之后就可能丢失。
如果业务对数据可靠性要求比较高,可以考虑引入本地持久化队列或者预写日志机制。
例如:
- 采集数据先写入可靠的本地持久化存储。
- 后台线程批量写入数据库。
- 数据库写入成功之后,更新对应的持久化状态。
- 程序重启时,检查是否存在尚未处理的数据。
当然,这并不意味着使用了数据库就绝对不会丢数据,仍然需要考虑事务提交、刷盘策略和断电场景。
另外,历史数据恢复和设备运行状态恢复是两个不同的问题。
设备重新连接之后,应该主动读取 PLC 当前的真实状态,而不是直接沿用断电之前的运行状态。
对于无法从设备或持久化存储中恢复的数据,也需要明确标记数据缺失。
三、下发控制指令时突然断开连接怎么办?
这道题我印象比较深。
面试官提出的场景是:
上位机正在向下位机发送控制指令,这个时候连接突然断开了,应该如何处理?
我当时的回答
我的思路是在后台通信线程中建立心跳检测机制。
如果检测到设备断开连接,就先缓存尚未完成的控制指令,等待心跳检测发现设备重新连接后,再将缓存的指令下发。
当时主要考虑的是断线重连和指令不丢失。
事后复盘
后来仔细想了一下,这个回答并不完整。
最大的问题是:连接断开不一定代表指令没有执行。
例如,上位机发送一条启动指令,下位机已经收到并执行,但是在返回响应的过程中通信断开。
此时如果上位机直接缓存并重发该指令,就可能造成重复执行。
因此,比较合理的处理方式应该是:
- 发生通信异常时,将当前指令标记为未确认状态。
- 记录指令内容、发送时间和异常信息。
- 建立断线检测与重连机制。
- 连接恢复后,先查询下位机实际状态。
- 根据指令类型、设备状态以及是否允许重复执行,决定是否重新下发。
对于普通的参数设置指令,如果重复执行不会产生额外影响,可以根据业务规则考虑重试。
但对于启动、复位、运动控制等操作,不能简单地在重连后自动重发。
尤其是在医疗设备软件中,控制指令的安全性可能比确保指令最终发送成功更加重要。
这也是这次面试让我印象最深的一道题。
通信恢复不等于业务状态恢复,指令发送成功也不等于设备执行成功。
四、明明创建了多线程,为什么 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++ 基础和算法能力确实还有不少需要补充的地方。
尤其是一些平时开发中不经常主动使用的知识,过了一段时间就容易忘记。
接下来准备重点加强:
- C++ 对象生命周期、智能指针、移动语义和 STL。
- Qt 事件循环、QThread、线程间通信与线程安全。
- Modbus 请求调度、多设备通信与超时处理。
- 数据持久化、断电恢复和控制指令状态管理。
- 基础算法和现场编码能力。
八、最后
目前这次面试的结果还没有出来。
说实话,面试结束后还是有点忐忑。
一方面,项目方面的大部分问题基本能够回答出来;另一方面,笔试算法没写出来,部分问题的回答也不算完整。
至于最终能不能通过,就只能等待后续通知了。
不过,相比单纯拿到一个面试结果,我觉得这次经历至少让我更清楚地认识到了自己目前的技术水平。
有一定的实际项目开发经验,但距离能够独立设计一套稳定、可靠的设备软件系统,还有不少需要学习的地方。
工作一年多,感觉自己正处在一个比较尴尬的阶段。
说完全没有经验吧,确实做过一些实际项目;说技术已经很成熟吧,很多基础和系统设计问题又还没有真正吃透。
但想想也正常,技术本来就是需要长期积累的东西。
先把基础补扎实,再把项目中遇到的问题研究透彻。
至于这次面试结果如何,等有消息再更新。
希望下一份工作,能够让我安安心心地写几年代码。
记录于 2026 年 11 月 6 日。