Python控制台版Cookie Clicker程序退出及后台CPS计算问题求助
问题概述
开发控制台文本交互版Cookie Clicker时遇到两个核心问题:
- 初始多线程实现:输入
exit命令无法终止程序,程序冻结,甚至Ctrl+C(Mac为Cmd+C)都无法结束,只能关闭终端,尝试sys.exit、os._exit等方法均无效。 - 改用单线程while循环后:CPS(每秒获得饼干数)仅在用户输入命令时才会增加,无法在后台持续运行。
初始多线程版本问题分析
- 递归调用导致线程无法正常退出:
add_cps_loop函数用递归代替循环,会导致线程栈不断累积,不仅可能触发栈溢出,还会让线程无法响应退出信号。 sys.exit()仅终止当前线程:get_user_input线程中调用sys.exit()只会终止该线程,add_cps_loop线程仍在运行,主线程也不会退出。- 未设置守护线程:创建线程时未标记为守护线程,主线程结束后子线程会继续运行,导致程序无法完全终止。
- 阻塞输入的信号处理问题:
input()处于阻塞状态时,信号(如Ctrl+C)可能无法被主线程正确捕获,导致程序无法响应中断。
单线程循环版本问题分析
while True循环中先执行get_user_input(),该函数会被input()阻塞,只有用户输入命令后才会执行add_cps_loop(),因此CPS的更新完全依赖用户输入,无法在后台持续累加饼干数。
修复后的完整代码
import time import random import threading import os import sys # 全局状态 cookies = 0.0 display_cookies = 0 cps = 74 cookies_per_click = 1 cookie_clicks = 0 running = True # 控制程序运行的标志 all_commands = { " cookie balance": "Displays the amount of cookies you have.\n", " exit": "Exits the program.\n", " view <item-to-view>": "Views the price (if available, otherwise, shows how to get it), item description, and item use (if available).\n", " click": "Clicks the cookie to gain cookies.\n" } def reset(): global cookies, cps, cookie_clicks, cookies_per_click cookies = 0.0 cps = 0 cookie_clicks = 0 cookies_per_click = 1 clear_console() def click(): global cookies, cookies_per_click, cookie_clicks double_chance = (random.random() * 100) == 28 form_of_word_cookie = "cookie" if cookies_per_click == 1 else "cookies" if double_chance: added = cookies_per_click * 2 cookies += added print(f"A glitch happened. We gave you {added} {form_of_word_cookie}, twice your cookies per click, to resolve it.") else: cookies += cookies_per_click print(f"Added {cookies_per_click} {form_of_word_cookie} to your bank.") cookie_clicks += 1 def add_cps_loop(): global cps, cookies, display_cookies while running: # 每秒累加cps个饼干,拆分到小间隔避免精度问题 cookies += cps / 100 # 每10ms更新一次 display_cookies = int(cookies) time.sleep(0.01) def clear_console(): os.system("cls" if os.name == "nt" else "clear") def format_all_commands(): all_commands_formatted = "" for command, desc in all_commands.items(): all_commands_formatted += f"{command}: {desc}" return all_commands_formatted def get_user_input(): global running while running: try: inp = input("Enter your command (Enter \"help\" for all commands): ").strip() match inp: case "clear": clear_console() case "click": click() case "cookie balance": print(display_cookies) case "cookies": print(f"{cookies:.2f}") case "cookie bal": print(display_cookies) case "exit": running = False print("Exiting...") sys.exit() case "help": print(format_all_commands()) case _: print("Command not recognized. Enter \"help\" for available commands.") except KeyboardInterrupt: running = False print("\nExiting...") sys.exit() if __name__ == "__main__": clear_console() # 创建守护线程,主线程结束时自动终止 cps_thread = threading.Thread(target=add_cps_loop, daemon=True) cps_thread.start() get_user_input()
关键修复说明
- 用循环替代递归:
add_cps_loop改用while running循环,避免递归栈溢出,同时通过running标志控制线程退出。 - 设置守护线程:创建CPS线程时添加
daemon=True,确保主线程退出时该线程自动终止。 - 全局运行标志:用
running变量统一控制所有线程的运行状态,exit命令和Ctrl+C都会设置该标志为False。 - 处理KeyboardInterrupt:在输入线程中捕获中断信号,优雅终止程序。
- 优化CPS更新频率:将CPS拆分到更小的时间间隔(10ms)更新,避免单次累加过大导致的精度问题,同时让饼干数更新更平滑。
内容的提问来源于stack exchange,提问作者yugms
相关产品推荐
相关产品推荐

