3、轮询模式原理:忙等待与休眠轮询
轮询,说白了就是「你不停地问:数据到了没?」。这是最原始、最直接的数据获取方式。我刚开始做量化系统那会儿,第一版行情模块就是纯轮询——简单粗暴,但坑也不少。
今天咱们把轮询模式拆开揉碎了讲。我会结合自己踩过的坑,聊聊忙等待、休眠轮询、周期选择,还有那个绕不开的CPU占用率与延迟的权衡。
3.1 忙等待(Busy Waiting)
忙等待,就是让CPU一直转,不停地检查某个条件是否满足。
// 忙等待示例
while (true) {
if (data_ready) {
process_data();
break;
}
// 啥也不干,继续循环
}
这段代码看着简单吧?但跑起来你就知道厉害了。CPU会以100%的占用率疯狂空转。我在早期的一个回测系统里用过这种模式,结果服务器风扇直接起飞,运维同事跑来问我是不是在挖矿。
忙等待唯一的优点就是延迟极低——条件一旦满足,立即就能响应。但代价太大了,现代系统基本不会这么干。
3.2 休眠轮询(Polling with Sleep)
既然忙等待太费CPU,那咱们让线程歇会儿行不行?这就是休眠轮询的思路。
// 休眠轮询示例
while (true) {
if (data_ready) {
process_data();
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
每次检查完,让线程睡10毫秒。CPU占用率瞬间从100%降到接近0%。但代价呢?延迟变高了——最坏情况下,数据到了你得等10毫秒才能处理。
我个人习惯把这种模式叫做「有节制的轮询」。它适合对延迟不敏感的场景,比如分钟级别的数据统计。但做高频行情?嗯,10毫秒的延迟够行情跑好几个来回了。
3.3 轮询周期的选择
轮询周期怎么定?这是个经典问题。选短了,CPU扛不住;选长了,延迟受不了。
我一般从三个维度来考虑:
| 场景 | 推荐周期 | 说明 |
|---|---|---|
| 高频行情 | 1-5毫秒 | 延迟敏感,CPU占用可接受 |
| 中频交易 | 10-50毫秒 | 平衡延迟和资源 |
| 低频统计 | 100-1000毫秒 | CPU占用优先 |
| 定时任务 | 秒级 | 对实时性要求不高 |
你可能会问:「能不能自适应?」当然可以。我做过一个自适应轮询器,根据最近100次数据到达的时间间隔动态调整周期。效果不错,但实现起来有点复杂。
3.4 CPU占用率与延迟的权衡
这个权衡,说白了就是「你要钱(低延迟)还是要命(低CPU)」。我见过不少团队在这个问题上纠结很久。
咱们画个图来理解:
从图上能看出来,两条曲线在某个点交叉了。这个交叉点就是咱们要找的平衡点。我个人习惯先确定延迟上限——比如行情处理不能超过5毫秒,然后在这个约束下尽量拉长轮询周期。
- 先做压力测试,摸清系统的延迟底线
- 设定一个可接受的延迟上限(比如行情延迟不超过2ms)
- 在这个上限下,选择最长的轮询周期
- 留20%的余量,防止突发流量
我曾经接手过一个项目,轮询周期设成了1毫秒,CPU长期跑在90%以上。一问原因,说是「怕延迟太高」。我做了个测试,把周期调到5毫秒,延迟只增加了不到1毫秒,CPU却降到了30%。你看,很多时候我们都在过度优化。
最后说一句:轮询模式虽然简单,但用好了威力不小。关键是找到那个「不浪费CPU,又不耽误事」的平衡点。下一节咱们聊聊更高级的事件驱动模式,那又是另一番天地了。
公众号:蓝海资料掘金营,微信deep3321