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

如何通过Telegram Bot立即终止Windows上的Python进程?

问题

用Python 3.11开发了一个Telegram Bot,用于控制Windows机器上运行的Python脚本。点击Stop按钮时,无法立即终止目标脚本进程,必须等脚本执行完毕才会响应停止指令,但直接在终端或其他Python脚本里用taskkill能正常终止该进程,怀疑是async/await的使用方式有误。

原因分析
  1. 同步阻塞占用事件循环:stop_command中使用了同步的subprocess.run,在异步环境下会阻塞整个事件循环,导致Bot无法及时处理终止后的后续逻辑,也无法响应其他请求。
  2. 输出读取逻辑未响应停止信号:send_output函数里的await stream.readline()是阻塞式调用,即使进程被终止,该调用可能不会立即返回,导致函数无法及时退出,run_script中的await asyncio.gather会一直等待输出流读取完成,才会执行后续的current_process.wait()。
解决方案

1. 异步执行taskkill,避免阻塞事件循环

将stop_command中的同步subprocess.run替换为异步的asyncio.create_subprocess_exec,确保事件循环不被阻塞。

2. 在输出读取循环中加入停止检查

修改send_output函数,每次循环先检查stop_requested状态,同时给stream.readline()设置超时,避免一直阻塞在无输出的场景。

3. 优化进程终止后的清理逻辑

确保进程被终止后,及时清理current_process并反馈状态给用户。

修改后的关键代码

调整stop_command为异步执行

@restricted
async def stop_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Stop the current process."""
    global current_process, stop_requested
    print("Stop button clicked.")
    if current_process is not None:
        try:
            # 改用异步方式执行taskkill
            kill_proc = await asyncio.create_subprocess_exec(
                'taskkill', '/F', '/T', '/PID', str(current_process.pid),
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE
            )
            stdout, stderr = await kill_proc.communicate()
            stop_requested = True
            print("Process killed.")
            await update.message.reply_text('Process killed.')
            # 打印taskkill输出
            print("Output of taskkill :")
            print(stdout.decode('utf-8', errors='ignore'))
            print(stderr.decode('utf-8', errors='ignore'))
            current_process = None
        except Exception as e:
            print(f"Error when stopping the process: {e}")
            await update.message.reply_text(f"Error when stopping the process: {e}")
    else:
        print("No processes running.")
        await update.message.reply_text('No running processes.')

修改send_output函数,加入停止检查

async def send_output(stream, update, context, stream_name):
    while True:
        # 每次循环先检查是否请求停止
        if stop_requested:
            break
        # 设置超时读取,避免一直阻塞
        try:
            line = await asyncio.wait_for(stream.readline(), timeout=1.0)
        except asyncio.TimeoutError:
            continue
        if not line:
            break
        try:
            decoded_output = line.decode('utf-8', errors='ignore').strip()
            print(f"{stream_name}: {decoded_output}")
            await context.bot.send_message(chat_id=update.effective_chat.id, text=f"{stream_name}: {decoded_output}")
        except UnicodeDecodeError as e:
            print(f"Decoding error : {e}")
    if stop_requested:
        print(f"{stream_name}: Process stopped by the user.")
        await context.bot.send_message(chat_id=update.effective_chat.id, text=f"{stream_name}: Process stopped by the user.")

调整run_script中的逻辑

await asyncio.gather(
    send_output(current_process.stdout, update, context, "STDOUT"),
    send_output(current_process.stderr, update, context, "STDERR"),
    return_exceptions=True  # 避免单个任务出错导致整个gather失败
)

if stop_requested:
    # 主动停止时直接清理进程并反馈
    current_process = None
    await context.bot.send_message(chat_id=update.effective_chat.id, text=f'Script stopped by the user.', reply_markup=reply_markup)
    return

return_code = await current_process.wait()
current_process = None
print(f"Script completed with return code : {return_code}")

if return_code != 0:
    await context.bot.send_message(chat_id=update.effective_chat.id, text=f'Script terminated with errors, return code : {return_code}', reply_markup=reply_markup)
else:
    await context.bot.send_message(chat_id=update.effective_chat.id, text=f'Script completed successfully, return code : {return_code}', reply_markup=reply_markup)
说明
  • asyncio.wait_for给stream.readline()设置超时,确保即使没有输出,也能定期检查stop_requested状态,避免一直阻塞。
  • return_exceptions=True保证单个输出流读取任务出错时,其他任务能继续执行,不会导致整个gather失败。
  • 异步执行taskkill确保事件循环能继续处理Bot的其他消息请求,提升响应速度。

内容的提问来源于stack exchange,提问作者Fokda

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 18:54:50