Twisted Python IRC bot - 如何异步执行函数以便它不会阻塞机器人?
Posted
技术标签:
【中文标题】Twisted Python IRC bot - 如何异步执行函数以便它不会阻塞机器人?【英文标题】:Twisted Python IRC bot - how to execute a function asynchronously so that it doesn't block the bot? 【发布时间】:2019-02-08 13:43:01 【问题描述】:我正在尝试编写一个 IRC 机器人,它在执行长时间(10 多秒)功能时继续正常工作。
我首先使用套接字编写机器人。当我调用一个“阻塞”函数(执行需要几秒钟的计算)时,机器人自然停止响应,并且在函数计算时没有记录聊天中发送的任何消息。
我做了一些谷歌搜索,看到很多人推荐使用 Twisted。
我实现了基本的 IRC 机器人,主要基于一些示例:
# twisted imports
from twisted.words.protocols import irc
from twisted.internet import reactor, protocol
from twisted.python import log
# system imports
import time, sys, datetime
def a_long_function():
time.sleep(180)
print("finished")
class BotMain(irc.IRCClient):
nickname = "testIRC_bot"
def connectionMade(self):
irc.IRCClient.connectionMade(self)
def connectionLost(self, reason):
irc.IRCClient.connectionLost(self, reason)
# callbacks for events
def signedOn(self):
"""Signed to server"""
self.join(self.factory.channel)
def joined(self, channel):
"""Joined channel"""
def privmsg(self, user, channel, msg):
"""Received message"""
user = user.split('!', 1)[0]
if 'test' in msg.lower():
print("timeout started")
a_long_function()
msg = "test finished"
self.msg(channel, msg)
if 'ping' in msg.lower():
self.msg(channel, "pong")
print("pong")
class BotMainFactory(protocol.ClientFactory):
"""A factory for BotMains """
protocol = BotMain
def __init__(self, channel, filename):
self.channel = channel
self.filename = filename
def clientConnectionLost(self, connector, reason):
"""Try to reconnect on connection lost"""
connector.connect()
def clientConnectionFailed(self, connector, reason):
print ("connection failed:", reason)
reactor.stop()
if __name__ == '__main__':
log.startLogging(sys.stdout)
f = BotMainFactory("#test", "log.txt")
reactor.connectTCP("irc.freenode.net", 6667, f)
reactor.run()
这种方法肯定比我之前的套接字实现要好,因为现在 bot 在执行 a_long_function() 时仍然接收发送的消息。
但是,它仅在功能完成后“看到”这些消息。这意味着当我将消息记录到 txt 文件时,在执行 a_long_function() 时收到的所有消息都会收到与函数完成时相同的时间戳 - 而不是在它们实际发送到聊天室时.
此外,机器人在执行 long 函数时仍然无法发送任何消息。
有人可以指出我应该如何更改代码的正确方向,以便可以异步执行这个长函数,以便机器人在执行时仍然可以记录和回复消息?
提前致谢。
编辑: 我遇到了this 答案,这让我想到我可以将 deferLater 调用添加到我的 a_long_function 中,以将其拆分为更小的块(也就是说需要 1 秒才能执行) ,并让机器人在两者之间恢复正常操作,以回复并记录同时发送到 IRC 频道的任何消息。或者可能添加一个计时器来计算 a_long_function 已经运行了多长时间,如果它超过阈值,它会调用 deferLater 让机器人赶上缓冲消息。
这确实有点像 hack 的想法 - 有没有更优雅的解决方案?
【问题讨论】:
【参考方案1】:不,没有更优雅的解决方案。除非您想使用线程,这可能看起来更优雅,但很容易导致程序不稳定。如果可以避免,请使用延期解决方案。
【讨论】:
【参考方案2】:要异步调用函数,您应该使用 asyncio 包以及 async/await 或协程。请记住,调用 async/await 是 v3 实现,而不是 v2。
使用异步/等待:
#!/usr/bin/env python3
# countasync.py
import asyncio
async def count():
print("One")
await asyncio.sleep(1)
print("Two")
async def main():
await asyncio.gather(count(), count(), count())
if __name__ == "__main__":
import time
s = time.perf_counter()
asyncio.run(main())
elapsed = time.perf_counter() - s
print(f"__file__ executed in elapsed:0.2f seconds.")
有一个非常好的教程,您可以阅读here,它使用 asyncio 进行了深入介绍。
希望有帮助!
【讨论】:
这与使用 Twisted 会神奇地使代码非阻塞的原始建议一样错误。asyncio.sleep
与deferLater
做同样的事情。 asyncio 或 Twisted 都没有神奇地使代码非阻塞。没有什么关于 async 或 await 或 coroutines 可以神奇地使代码成为非阻塞的。将代码重写为非阻塞是使其成为非阻塞的原因。以上是关于Twisted Python IRC bot - 如何异步执行函数以便它不会阻塞机器人?的主要内容,如果未能解决你的问题,请参考以下文章
Twisted Python IRC bot - 如何异步执行函数以便它不会阻塞机器人?