【问题标题】:How to create a function for a command that processes concurrently如何为同时处理的命令创建函数
【发布时间】:2021-12-30 17:32:24
【问题描述】:

当我在 discord 中输入命令 !sleeper 时,紧跟在输入 !hello 之后。该机器人基本上暂停了 10 秒,因为它正在处理 !sleeper。 10 秒后,它发送消息I have been sleeping for 10 seconds,然后立即发送Hello partner!。如果有人发送!sleeper 命令,我怎样才能使整个机器人不会“暂停”。

现在发生了什么:

  1. 我输入!sleeper
  2. 我输入!hello
  3. 机器人等待 9-10 秒
  4. 机器人发送I have been sleeping for 10 seconds
  5. 机器人发送Hello partner!

我想要什么:

  1. 我输入!sleeper
  2. 我输入!hello
  3. 机器人发送Hello partner!
  4. 机器人等待 9-10 秒
  5. 机器人发送I have been sleeping for 10 seconds

PS:我写了“等待 9-10 秒”,因为我需要大约一秒钟才能输入 !hello

import time

from discord.ext import commands


class Hello(commands.Cog):
    def __init__(self, client):
        self.client = client

    @commands.Cog.listener()
    async def on_ready(self):
        print(f'{self.__class__.__name__} Cog is ready')

    @commands.command()
    async def hello(self, ctx):
        await ctx.send('Hello partner!')

    @commands.command()
    async def sleeper(self, ctx):
        await self.sleep_now()
        await ctx.send('I have been sleeping for 10 seconds')

    async def sleep_now(self):
        time.sleep(10)

def setup(client):
    client.add_cog(Hello(client))

【问题讨论】:

    标签: python-3.x async-await discord discord.py


    【解决方案1】:

    您可以尝试使用后台任务来实现此目的。我不确定这是否是最好或最干净的解决方案,但您可以找到更多文档here

    from discord.ext import tasks, commands
    
    
    class Hello(commands.Cog):
        def __init__(self, client):
            self.client = client
            # flag variable to keep track of if we are waiting for a response from sleeper
            self.sleeping = 0
    
        @commands.Cog.listener()
        async def on_ready(self):
            print(f'{self.__class__.__name__} Cog is ready')
    
        @commands.command()
        async def hello(self, ctx):
            await ctx.send('Hello partner!')
    
        @commands.command()
        async def sleeper(self, ctx):
            # if the task is already running, ie. someone already called sleeper
            # you cannot start a background task if it is already running, it will cause an error
            if self.sleep.is_running():
                await ctx.send('Already sleeping!')
            else:
                # start the background task
                self.sleep.start(ctx)
    
        # The loop runs 2 times
        # It runs immediately when you start the task (when you type !sleeper) and then again in increments of the time specified
        # I use a flag to check when to send the message and cancel the task (after the first ten seconds)
        # cancelling the task stops it from sending additional messages every ten seconds
        @tasks.loop(seconds=10.0)
        async def sleep(self, ctx):
            # helper method to stop the background task right after it sends the message
            async def stop_sleep():
                self.sleep.cancel()
            # checks to see if the task is sleeping or not
            if self.sleeping == 1:
                # reset the value
                self.sleeping = 0
                # send the message
                await ctx.send('I have been sleeping for 10 seconds')
                # stop the task
                await stop_sleep()
            else:
                # keep track of it sleeping
                self.sleeping = 1
    
    
    def setup(client):
        client.add_cog(Hello(client))
    

    编辑:

    有一个很多更好的方法来解决您的问题,而无需使用后台任务。使用 asyncio 代替睡眠时间。

    from discord.ext import commands
    import asyncio
    
    class Hello(commands.Cog):
        def __init__(self, client):
            self.client = client
    
        @commands.Cog.listener()
        async def on_ready(self):
            print(f'{self.__class__.__name__} Cog is ready')
    
        @commands.command()
        async def hello(self, ctx):
            await ctx.send('Hello partner!')
    
        @commands.command()
        async def sleeper(self, ctx):
            await asyncio.sleep(10)
            await ctx.send('I have been sleeping for 10 seconds')
    

    【讨论】:

    • 任务不是我要找的。 sleep_now() 函数代表一个辅助非discord函数,需要一段时间来处理。所以本质上,我如何制作一个可以在命令函数中调用并保持并发的自定义函数。所以是的,我可以使用await asyncio.sleep(10),但是如果我有一个需要 10 秒的自定义流程呢?想象一下,sleep_now() 并不是一个真正的睡眠函数,而是一个需要一段时间才能完成的过程
    【解决方案2】:

    根据您对 Kyle 回答的评论,您希望异步处理函数。

    那么你要实现的是一个线程。您可以创建一个线程并在线程内休眠 10 秒(或执行您的耗时任务),然后打印“我已经休眠了 10 秒”。因此它不会阻止 !hello 和其他命令的执行。它看起来像这样

    这将是昂贵任务的功能

    async def thread_function(ctx):
        time.sleep(10)
        await ctx.send('I have been sleeping for 10 seconds')
    

    你会这样称呼它

     x = threading.Thread(target= thread_function, args=(ctx))
    

    或者,您可以使用事件循环,这是一个更高级的主题。如果你愿意,你可以研究它,但它本质上所做的是它评估你在运行时调用的函数并独立执行它而不创建新线程。我强烈建议您检查一下。 然而,这就是它可能的样子

    from concurrent.futures import ThreadPoolExecutor
    loop = asyncio.get_event_loop()
    _executor = ThreadPoolExecutor(1)
    await loop.run_in_executor(_executor, thread_function, ctx)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-13
      • 2016-01-24
      • 1970-01-01
      • 2015-11-11
      • 1970-01-01
      • 2011-08-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多