如何使用asyncio作为模块的一部分设置后台进程

时间:2018-06-20 21:32:19

标签: python python-3.x asynchronous async-await python-asyncio

我正在编写一个python模块(实验用例),该模块使用户能够将消息发送到另一台计算机。我们要将消息发送到的计算机具有一个未知的地址,该地址会在任意时间更改。还有另一台计算机(中间设备)提供地址和时间,直到地址过期。

为简单起见,我们将发送消息的计算机称为 computer-A ,将接收消息的计算机称为 computer-B computer-C 我们需要联系中介以获取计算机B的地址。

我要完成的任务:

我希望能够将等待过期时间的过程推迟到asyncio.sleep()中。时间到了,我希望该过程重新获得对事件循环的控制,并运行一个函数来更新地址。

我正在努力解决的问题是如何在无法调用run_until_complete / run_forever的类中实现此方法(或者我公然不正确)。您如何使用asyncio框架实现这样的事情?

示例性代码示例?:

from some_random_messaging_service import deliver_msg

INTERMEDIARY_COMPUTER_C_ADDRESS = "some.random.address"

class CustomMessagingSystem:

    def __init__(self, computer=None):
        """Constructor

        :param computer: computer name

        """
        self.addresses = {}
        if computer:
            self.get_address(computer)

    def get_address(self, computer):
        """Gets address from computer-C (intermediary)

        :param computer: computer name

        """
        self.addresses[computer] = self.find_address(INTERMEDIARY_COMPUTER_C_ADDRESS, computer)
        await self.update_address(computer, self.expiration_time(computer))

    def expiration_time(self, computer):
        """
        :param computer: computer name
        :return: address expiration time in seconds
        """
        return self.addresses[computer][1]

    def address(self, computer):
        """

        :param computer: computer name
        :return: computer address

        """
        return self.addresses[computer][0]

    async def update_address(self, computer, expiration_time):
        """updates address of computer after given expiration_time

        :param computer: computer name
        :expiration_time: expiration time in seconds

        """
        asyncio.sleep(expiration_time)
        self.get_address(computer)

    def send_message(self, computer, message):
        """Sends message to target computer

        :param computer: computer name
        :param message: UTF-8 message

        """
        deliver_msg(self.address(computer), message)

2 个答案:

答案 0 :(得分:1)

完成此操作的一种方法是让您的班级使用create_task在事件循环上安排任务。 这可以在事件循环开始之前或之后完成。

由于您希望每个地址都有一个单独的计时器,因此每个地址只有一个任务是最简单的; 我们可以将它们放在地址旁边的字典中:

class CustomMessagingSystem:

    def __init__(self, computer=None, ioloop=asyncio.get_event_loop()):
        self.addresses = {}
        self.updaters = {}
        self.ioloop = ioloop
        if computer:
            self._add_address(computer)

    def get_address(self, computer):
        try:
            return self.addresses[computer]
        except KeyError:
            self._add_address(computer)
            return self.addresses[computer]

    def _add_address(self, computer):
            address = self.find_address(INTERMEDIARY_COMPUTER_C_ADDRESS, computer)
            task = self.ioloop.create_task(self._update_address, computer)
            self.updaters[computer] = task
            self.addresses[computer] = address

    async def _update_address(self, computer):
        while True:
            addr, expiration_time = self.find_address(INTERMEDIARY_COMPUTER_C_ADDRESS, computer)
            self.addresses[addr] = addr
            await asyncio.sleep(expiration_time)

    def send_message(self, computer, message):
        deliver_msg(self.get_address(computer), message)

自然,如果事件循环从未开始,那么更新将永远不会发生。

最后,您要做的是控制这些更新程序任务的生存期。 为了简化起见,我在上面的示例中并未实现此目的。 标准方法是使您的类成为上下文管理器,并获得__exit__ 取消所有更新程序。

答案 1 :(得分:0)

asyncio程序的一般流程是这样的。

async def long_io_bound_operation():
    await <call some async function that does your work>
    ...

def main():
    asyncio.get_event_loop().run_until_complete(long_io_bound_operation())

还有其他方法可以根据您的需要实际等待函数调用long_io_bound_operation()返回的协程,但这是它的主要形式。阅读有关asyncio模块的详细信息,但要点是,每次使用await关键字时,python运行时都可以选择非阻塞等待结果,而不是阻塞并旋转等待一些工作要做。

从您的代码中对我来说还不清楚您打算为此通信调用什么协议,但这是一个很好的选择,因为围绕该协议已经编写了一个异步兼容包装供您使用。例如,aiohttp是HTTP请求的异步包装器。

如果您提供有关所使用协议的更多详细信息,则可能会针对您的问题获得更具体的建议。希望此概述对您有用。