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

在Tkinter中结合Pyrogram与多线程时触发报错问题求助

解决Pyrogram客户端无法正确停止、重复启动报错的问题

错误截图

问题描述

点击「消息发送」按钮时消息发送正常,点击「停止」按钮后程序看似停止,但Pyrogram客户端并未真正终止,再次点击发送按钮时触发报错。

代码问题分析

  • 停止方法未正确执行:原代码中app.stop仅引用方法未调用,且app.stop()是异步方法,无法在同步的stop函数中直接执行。
  • 循环无法立即终止:async for遍历聊天历史时,即使run设为False,仍会继续遍历剩余条目,无法快速终止流程。
  • 重复启动冲突:同一个Client实例被多次调用start(),前一次客户端未完全释放资源,导致重复启动报错。
  • 事件循环管理混乱:复用事件循环且未处理线程间异步调用逻辑,导致后续启动时循环状态异常。

修正后的代码

from customtkinter import *
import customtkinter as ctk
from threading import Thread
import os, asyncio
from pyrogram import Client
from pyrogram.enums import MessageMediaType

run = True
path = os.getcwd()
app = Client(path + "\\users\\" + "user")
tg_task = None  # 保存异步任务对象,用于后续取消操作

async def tg_bot():
    global run
    global app, tg_task
    s = 0
    # 仅在客户端未连接时启动
    if not app.is_connected:
        await app.start()
    try:
        async for ii in app.get_chat_history(-1002070231241):
            if not run:
                break  # 立即终止遍历循环
            if ii.media == MessageMediaType.VIDEO:
                s += 1
                await app.copy_message(-1002142940185, -1002070231241, ii.id, caption="")
                print(f"{s} sended.")
                await asyncio.sleep(1)
    finally:
        # 确保客户端最终停止
        if app.is_connected:
            await app.stop()
        tg_task = None

def start():
    global run, tg_task
    run = True
    # 已有运行任务时,避免重复启动
    if tg_task is not None and not tg_task.done():
        return

    def run_loop():
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        global tg_task
        tg_task = loop.create_task(tg_bot())
        try:
            loop.run_until_complete(tg_task)
        finally:
            loop.close()

    oto = Thread(target=run_loop)
    oto.daemon = True
    oto.start()

def stop():
    global run, tg_task, app
    run = False
    # 线程安全地取消异步任务
    if tg_task is not None and not tg_task.done():
        asyncio.call_soon_threadsafe(tg_task.cancel)
    # 线程安全地停止客户端
    if app.is_connected:
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        loop.run_until_complete(app.stop())

root = ctk.CTk()
root.geometry("300x300")
basla = CTkButton(root, text="消息发送", command=start)
basla.place(x=75, y=100)

dur = CTkButton(root, text="停止", command=stop)
dur.place(x=75, y=200)

root.mainloop()

关键修改说明

  • 正确触发客户端停止:在stop函数中通过独立事件循环线程安全调用await app.stop(),确保客户端资源完全释放。
  • 任务取消逻辑:用tg_task追踪异步任务,点击停止时直接取消任务,让流程快速终止并执行客户端停止操作。
  • 防重复启动:启动前检查任务状态,避免同一客户端实例被重复启动。
  • 快速终止循环:在async for循环内实时判断run状态,一旦触发停止立即跳出循环,提升响应速度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 04:51:23