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

如何让Discord机器人与while循环同时运行?

解决Discord机器人与Blender渲染任务并发问题

核心问题分析

  • bot.run()是阻塞调用,会持续占用事件循环,导致后续渲染代码无法执行
  • 直接用asyncio.gather()出错,本质是未正确共享事件循环,或同步渲染任务阻塞了异步循环

修正后的完整代码示例

以下代码将机器人与渲染任务整合到同一异步环境,同时通过线程池处理同步的Blender渲染,避免阻塞事件循环:

import asyncio
import discord
from discord.ext import commands
import subprocess
import time
from concurrent.futures import ThreadPoolExecutor

# 初始化Discord Bot(v2+版本需显式设置Intents)
intents = discord.Intents.default()
intents.message_content = True  # 必须开启消息内容权限才能接收命令
bot = commands.Bot(command_prefix='!', intents=intents)

# 共享渲染状态变量(简单场景用全局变量,复杂场景可改用线程安全类)
render_start_time = None
current_task = None
total_tasks = 0
completed_tasks = 0

@bot.event
async def on_ready():
    print(f'Logged in as {bot.user.name}')

@bot.command()
async def progress(ctx):
    global render_start_time, completed_tasks, total_tasks, current_task
    if not render_start_time:
        await ctx.send('渲染尚未开始')
        return
    
    elapsed = time.time() - render_start_time
    avg_time_per_task = elapsed / completed_tasks if completed_tasks > 0 else 0
    remaining_tasks = total_tasks - completed_tasks
    estimated_remaining = avg_time_per_task * remaining_tasks
    
    msg = (
        f'渲染进度:{completed_tasks}/{total_tasks}\n'
        f'已用时间:{elapsed:.2f}秒\n'
        f'当前任务:{current_task}\n'
        f'预计剩余时间:{estimated_remaining:.2f}秒'
    )
    await ctx.send(msg)

async def run_bot():
    await bot.start('YOUR_DISCORD_BOT_TOKEN')

# 同步Blender渲染函数(放到线程池执行,避免阻塞异步循环)
def render_blender_file(file_path):
    global current_task
    current_task = file_path
    # 替换为你的Blender命令行参数
    result = subprocess.run(
        ['blender', '-b', file_path, '-o', 'render_output/', '-F', 'PNG', '-f', '1'],
        capture_output=True, text=True
    )
    if result.returncode != 0:
        print(f'渲染失败:{file_path}\n错误信息:{result.stderr}')
    return result.returncode == 0

async def render_queue(file_list):
    global render_start_time, total_tasks, completed_tasks
    render_start_time = time.time()
    total_tasks = len(file_list)
    completed_tasks = 0
    
    # 用线程池执行同步渲染任务,max_workers设为1保证按队列顺序执行
    with ThreadPoolExecutor(max_workers=1) as executor:
        for file_path in file_list:
            success = await asyncio.get_event_loop().run_in_executor(executor, render_blender_file, file_path)
            if success:
                completed_tasks += 1
            print(f'完成任务 {completed_tasks}/{total_tasks}:{file_path}')
    
    print('所有渲染任务完成')

async def main():
    # 将机器人和渲染任务加入同一事件循环
    bot_task = asyncio.create_task(run_bot())
    # 替换为你的Blender文件列表
    render_task = asyncio.create_task(render_queue(['file1.blend', 'file2.blend', 'file3.blend']))
    
    # 等待两个任务完成(机器人会持续运行,直到出错或被中断)
    await asyncio.gather(bot_task, render_task)

if __name__ == '__main__':
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print('程序被中断')

关键要点说明

  • 避免阻塞事件循环:Blender的subprocess.run()是同步阻塞调用,必须通过ThreadPoolExecutor和run_in_executor在异步环境中执行,防止卡住机器人的消息处理。
  • 状态同步:用全局变量存储渲染进度,确保!progress命令能获取实时数据;复杂场景可改用线程安全的队列或类来管理状态。
  • 正确启动异步任务:用asyncio.create_task()将机器人和渲染任务加入同一事件循环,再通过asyncio.gather()等待任务完成,替代阻塞的bot.run()。
  • Discord权限配置:discord.py v2+需在开发者后台开启message_content权限,否则机器人无法接收消息命令。

常见问题排查

  • "attached to a different loop"错误:确保所有异步任务都由asyncio.run()统一管理的事件循环执行,不要手动创建多个循环。
  • 机器人无响应:检查是否有同步代码阻塞了事件循环,所有耗时操作(渲染、文件IO等)必须放到线程/进程池执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 03:55:37