任务的循环退出已被销毁,但正在等待处理

这是我的python程序的相关代码:

import discord
import asyncio

class Bot(discord.Client):
    def __init__(self):
        super().__init__()

    @asyncio.coroutine
    def my_background_task(self):
        yield from self.wait_until_ready()
        while not self.is_closed:
            yield from asyncio.sleep(3600*24) # <- This is line 76 where it fails
            doSomething()

bot = Bot()
loop = asyncio.get_event_loop()
try:
    loop.create_task(bot.my_background_task())
    loop.run_until_complete(bot.login('username', 'password'))
    loop.run_until_complete(bot.connect())
except Exception:
    loop.run_until_complete(bot.close())
finally:
    loop.close()

该程序偶尔退出(本身,而不应该),没有其他错误或警告以外

Task was destroyed but it is pending!
task: <Task pending coro=<my_background_task() running at bin/discordBot.py:76> wait_for=<Future pending cb=[Task._wakeup()]>>

如何确保程序不会随机退出? 我在Xubuntu 15.10上安装了Python 3.4.3+。


这是因为不和谐客户端模块每分钟需要控制一次。

这意味着任何超过一定时间的窃取控制的函数都会导致不和谐的客户端进入无效状态(稍后可能会在客户端的下一个方法调用时显示为异常)。

为了确保不一致模块客户端可以ping不和服务器,您应该使用真正的多线程解决方案。

一种解决方案是将所有繁重的处理卸载到单独的进程上(单独的线程不会这样做,因为Python具有全局解释器锁),并使用不协调bot作为负责填充工作队列的薄层。

相关阅读:https://discordpy.readthedocs.io/en/latest/faq.html#what-does-blocking-mean

示例解决方案...这是超出问题范围的方式,但我已经编写了代码。 如果我有更多的时间,我会写一个更短的解决方案:)

2部分,不和谐交互和处理服务器:

这是不和谐的听众。

import discord
import re
import asyncio
import traceback

import websockets
import json

# Call a function on other server
async def call(methodName, *args, **kwargs):
    async with websockets.connect('ws://localhost:9001/meow') as websocket:
        payload = json.dumps( {"method":methodName, "args":args, "kwargs": kwargs})
        await websocket.send(payload)
        #...
        resp = await websocket.recv()
        #...
        return resp

client = discord.Client()
tok = open("token.dat").read()

@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)
    print(client.user.id)
    print('------')

@client.event
async def on_error(event, *args, **kwargs):
    print("Error?")

@client.event
async def on_message(message):
    try:
        if message.author.id == client.user.id:
            return
        m = re.match("(w+) for (d+).*?", message.content)
        if m:
            g = m.groups(1)
            methodName = g[0]
            someNumber = int(g[1])
            response = await call(methodName, someNumber)
            if response:
                await client.send_message(message.channel, response[0:2000])
    except Exception as e:
        print (e)
        print (traceback.format_exc())

client.run(tok)

这是处理大量请求的工作服务器。 您可以使这部分同步或异步。

我选择使用一种叫做websocket的魔法来将数据从一个python进程发送到另一个python进程。 但是你可以使用任何你想要的东西。 例如,您可以让一个脚本将文件写入目录,另一个脚本可以读取文件并处理它们。

import tornado
import tornado.websocket
import tornado.httpserver
import json
import asyncio
import inspect
import time

class Handler:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def consume(self, text):
        return "You said {0} and I say hiya".format(text)

    async def sweeps(self, len):
        await asyncio.sleep(len)
        return "Slept for {0} seconds asynchronously!".format(len)

    def sleeps(self, len):
        time.sleep(len)
        return "Slept for {0} seconds synchronously!".format(len)


class MyService(Handler, tornado.websocket.WebSocketHandler):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def stop(self):
        Handler.server.stop()

    def open(self):
        print("WebSocket opened")

    def on_message(self, message):
        print (message)
        j = json.loads(message)
        methodName = j["method"]
        args = j.get("args", ())

        method = getattr(self, methodName)
        if inspect.iscoroutinefunction(method):
            loop = asyncio.get_event_loop()
            task = loop.create_task(method(*args))
            task.add_done_callback( lambda res: self.write_message(res.result()))
            future = asyncio.ensure_future(task)

        elif method:
            resp = method(*args)
            self.write_message(resp)

    def on_close(self):
        print("WebSocket closed")

application = tornado.web.Application([
    (r'/meow', MyService),
])

if __name__ == "__main__":
    from tornado.platform.asyncio import AsyncIOMainLoop
    AsyncIOMainLoop().install()

    http_server = tornado.httpserver.HTTPServer(application)
    Handler.server = http_server
    http_server.listen(9001)

    asyncio.get_event_loop().run_forever()

现在,如果你在不同的python脚本中运行这两个进程,并告诉你的机器人“睡100天”,它会快乐地睡100秒! asyncio的功能就像是一个转换工作队列,你可以通过将它们作为单独的python脚本运行来正确地将监听器与后端处理分开。

现在,无论您的函数在“服务器”部分运行多久,客户端部分都不会被阻止对不一致服务器执行ping操作。

图像未能上传,但是...无论如何,这是如何告诉机器人睡觉并回复...请注意,睡眠是同步的。 http://i.imgur.com/N4ZPPbB.png


我认为asyncio.sleep没有问题。 无论如何,你不应该压制你得到的异常:

bot = Bot()
loop = asyncio.get_event_loop()
try:
    # ...
except Exception as e:
    loop.run_until_complete(bot.close())
    raise e  # <--- reraise exception you got while execution to see it (or log it here)
finally:
    # ...

您必须在退出时手动停止您的任务:

import discord
import asyncio

class Bot(discord.Client):
    def __init__(self):
        super().__init__()

    @asyncio.coroutine
    def my_background_task(self):
        yield from self.wait_until_ready()
        while not self.is_closed:
            yield from asyncio.sleep(3600*24) # <- This is line 76 where it fails
            doSomething()

bot = Bot()
loop = asyncio.get_event_loop()
try:
    task = loop.create_task(bot.my_background_task())
    loop.run_until_complete(bot.login('username', 'password'))
    loop.run_until_complete(bot.connect())
except Exception:
    loop.run_until_complete(bot.close())
finally:
    task.cancel()
    try:
        loop.run_until_complete(task)
    except Exception:
        pass 
    loop.close()
链接地址: http://www.djcxy.com/p/32779.html

上一篇: Loop exits with Task was destroyed but it is pending

下一篇: Maven: compile OK, package OK, compile + package fails