Python骰子游戏添加线程倒计时器遇RuntimeError问题求助
解决Python骰子游戏倒计时线程重复启动错误
错误原因
你遇到的RuntimeError: threads can only be started once是因为同一个Thread对象只能调用一次start()方法。如果之前尝试复用了同一个线程实例处理多轮玩家回合,就会触发这个错误。
修复方案
通过以下几点修复问题:
- 用
threading.Event替代全局done变量,更安全地控制线程终止 - 每次玩家回合都创建新的Thread实例,避免重复启动同一线程
- 处理倒计时结束的情况:玩家未及时输入则判定该回合失败(不得分)
修改后的完整代码
import random import os import time from threading import Thread, Event class Player: def __init__(self, name) -> None: self.name = name self.score = 0 def add_points(self, points): self.score += points def roll_dice(): min_val = 1 max_val = 6 dice_value = random.randint(min_val, max_val) return dice_value def countdown(user_time, stop_event): while user_time >= 0 and not stop_event.is_set(): mins, secs = divmod(user_time, 60) timer = '{:02d}:{:02d}'.format(mins, secs) print(timer, end='\r') time.sleep(1) user_time -= 1 # 倒计时结束后主动设置事件,通知主线程 if not stop_event.is_set(): stop_event.set() def player_turn(player, countdown_time=10): os.system('cls' if os.name == 'nt' else 'clear') print(f'Hurry up {player.name}! You have {countdown_time} seconds to press Enter.') stop_event = Event() # 每次回合创建全新的线程实例 timer_thread = Thread(target=countdown, args=(countdown_time, stop_event)) timer_thread.start() # 等待用户输入或倒计时结束 try: input('Press Enter to roll the dice\n') stop_event.set() timer_thread.join() roll = roll_dice() player.add_points(roll) print(f'{player.name} rolled a {roll}! Total score: {player.score}') except: stop_event.set() timer_thread.join() print(f'{player.name} ran out of time! No points added.') if __name__ == '__main__': player1_name = input('Enter player 1 name: ') player2_name = input('Enter player 2 name: ') player1 = Player(player1_name) player2 = Player(player2_name) roll_again = 'y' while roll_again.lower() in ['y', 'yes']: player_turn(player1) input('\nPress Enter to switch to next player...') player_turn(player2) input('\nPress Enter to continue...') if player1.score >= 30: print(f'\nGame Over! {player1.name} wins with {player1.score} points!') break elif player2.score >= 30: print(f'\nGame Over! {player2.name} wins with {player2.score} points!') break roll_again = input('\nRoll again? (y/n): ')
关键修改点说明
threading.Event的使用:替代全局done变量,通过set()和is_set()在主线程与倒计时线程间安全传递终止信号,避免全局变量的线程安全问题。- 每次创建新线程:在
player_turn函数内,每轮玩家回合都初始化新的Thread实例,确保每次调用start()都是全新线程,不会触发重复启动错误。 - 回合流程封装:将玩家回合逻辑封装到
player_turn函数中,代码更整洁且便于复用。 - 超时处理:倒计时结束后若用户未输入,
stop_event会被触发,主线程检测后判定该回合失败,不给玩家加分。
内容的提问来源于stack exchange,提问作者Demeridianth
相关产品推荐
相关产品推荐

