You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 13:42:46