我正在编写一个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)
答案 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请求的异步包装器。
如果您提供有关所使用协议的更多详细信息,则可能会针对您的问题获得更具体的建议。希望此概述对您有用。