025、while 循环的正确姿势:死循环预防、哨兵模式、交互式菜单
上周帮一个刚入行的同事排查线上问题,发现他的爬虫脚本跑了三天后把服务器CPU打满了。一看代码,一个while True循环里没有break条件,连个sleep都没有。这种问题在初学者代码里太常见了——while循环看似简单,但用不好就是一颗定时炸弹。
死循环:不是你想停就能停
先看一个典型的“翻车现场”:
# 别这样写!我见过有人把这段代码部署到生产环境
count = 0
while count < 10:
print("正在处理第", count, "个任务")
# 这里踩过坑:忘记更新计数器
# count += 1 # 注释掉这行,循环永远跑不完
这段代码会无限打印“正在处理第 0 个任务”。count永远等于0,条件永远为True。CPU会一直飙升,直到你手动kill进程或者服务器重启。
死循环的三种常见死法:
- 忘记更新循环变量——上面那个例子就是典型
- 条件永远为True——比如
while 1:或者while True:却没有break - 逻辑错误导致条件无法满足——比如
while x > 0:但x一直在增加
我自己的经验是:写while循环时,先写退出条件,再写循环体。就像写代码前先想好怎么收尾一样。
# 正确的姿势:先确认退出机制
count = 0
while count < 10:
print("正在处理第", count, "个任务")
count += 1 # 这里踩过坑,但这次记住了
哨兵模式:让循环自己喊停
哨兵模式是我最喜欢用的while循环写法。所谓哨兵,就是一个特殊值,当它出现时循环就结束。
# 哨兵模式:用户输入'quit'时退出
while True:
user_input = input("请输入命令(输入quit退出):")
if user_input == 'quit': # 'quit'就是哨兵
print("收到退出指令,拜拜")
break
print("你输入了:", user_input)
这个模式在处理文件读取时特别香:
# 读取文件直到遇到空行(空行就是哨兵)
with open('config.txt', 'r') as f:
while True:
line = f.readline()
if not line: # 空字符串就是哨兵
break
print("配置项:", line.strip())
哨兵模式的核心思想:用一个不可能出现在正常数据中的值作为终止信号。比如处理用户输入时用’quit’,处理数字时用-1,处理字符串时用空字符串。
交互式菜单:while循环的实战场景
命令行工具里最常见的场景就是交互式菜单。我写过不下20个这样的工具,每次都用同一个模板:
def show_menu():
print("\n=== 系统管理工具 ===")
print("1. 查看系统状态")
print("2. 清理缓存")
print("3. 重启服务")
print("0. 退出")
def main():
while True:
show_menu()
choice = input("请选择操作:")
if choice == '1':
print("正在查看系统状态...")
# 这里踩过坑:忘记加延时,导致CPU占用过高
# time.sleep(0.1)
elif choice == '2':
print("正在清理缓存...")
elif choice == '3':
print("正在重启服务...")
elif choice == '0':
print("感谢使用,再见!")
break
else:
print("无效选择,请重新输入")
if __name__ == '__main__':
main()
这个模板我用了好几年,有几个关键点:
- 菜单显示放在循环里:每次操作完都能看到菜单
- 用break退出:而不是修改循环条件
- 处理无效输入:防止用户乱按导致程序崩溃
实战经验:while循环的五个铁律
-
永远不要写裸的while True——至少加个sleep或者明确的break条件。我见过最离谱的代码是
while True: pass,直接把CPU干到100%。 -
哨兵值要选对——处理用户输入时,‘quit’、‘exit’、'q’都要考虑。处理数字时,-1或者0通常是不错的选择。
-
循环体内要有延时——特别是轮询场景,比如检查文件是否生成、等待API响应。
time.sleep(0.1)就能让CPU从100%降到1%。 -
考虑超时机制——有些循环可能因为外部原因永远跑不完。加个超时计数器,超过一定次数就强制退出。
# 超时保护:防止死循环
max_retries = 5
retry_count = 0
while retry_count < max_retries:
try:
result = api_call()
if result:
break
except Exception as e:
print(f"第{retry_count+1}次调用失败:{e}")
retry_count += 1
time.sleep(1)
- 日志记录——生产环境里的while循环一定要加日志。不然出了问题你都不知道循环卡在哪一步。
个人经验
写while循环这么多年,最大的感悟是:循环越简单越好。复杂的业务逻辑不要塞进循环体,拆成函数。循环只负责“什么时候继续”和“什么时候停止”,具体做什么交给函数去处理。
还有一个容易被忽视的点:while循环里的异常处理。如果循环体里抛了异常没捕获,整个循环就挂了。我习惯在循环体最外层加个try-except,至少保证异常不会让循环意外终止。
最后说一句:能用for循环解决的问题,别用while。for循环天然有边界,while循环需要你自己维护边界。少一个变量就少一个bug源。

1767

被折叠的 条评论
为什么被折叠?



