与irc.bot.SingleServerIRCBot保持一致的线程(与twitch一起使用)

时间:2018-10-31 05:12:20

标签: python multithreading irc twitch

将断开连接信号发送到包含SingleServerIRCBot的线程的正确方法是什么?

我正在实例化通过

连接到抽搐的机器人
import threading
import irc.bot
class MyBot(irc.bot.SingleServerIRCBot):
    ...
bot = MyBot(...)
threads = []
t = threading.Thread(target=bot.start()
threads.append(t)
t.start()

当流不再存在时,无论我尝试了什么,我都无法使线程成功结束。我应该如何发送信号到线程,告诉它退出通道,杀死机器人,然后杀死机器人?

.start方法的代码可以在https://github.com/jaraco/irc/blob/master/irc/bot.py#L331

中找到

我的第一个想法是使用具有退出条件的while循环覆盖该方法。到目前为止,我还没有运气。

此外,https://github.com/jaraco/irc/blob/master/irc/bot.py#L269处有一个.die方法,但是当线程执行无限循环时如何调用该方法?

试图直接杀死线程最终导致它们持续存在,并最终引发有关进程正在运行的线程总数的错误。

为赏金而编辑:我也将接受一个答案,该答案描述了一次处理多个IRC机器人的更好方法。

1 个答案:

答案 0 :(得分:0)

我不认为您可以(或应该)直接杀死线程,但是您可以停止在该线程上运行的任务。然后该线程将处于非活动状态,并且您可以根据需要将其从线程列表中删除。我对SingleServerIRCBot不熟悉,但是我将使用下面的类作为示例。

import time

class MyTask:
    def __init__(self):
        self._active = True

    def start(self):
        while self._active:
            print('running')
            time.sleep(1)

    def die(self):
        self._active = False

在Python3中,线程具有_target属性,我们可以从中访问目标函数/方法。我们可以使用此属性访问目标的对象并调用die方法(例如:thread._target.__self__.die())。但是,我认为最好继承Thread的子类并将目标对象存储在变量中,因为_target是私有属性,也是出于兼容性原因。

import threading

class MyThread(threading.Thread):
    def __init__(self, target, args=()):
        super(MyThread, self).__init__()
        self.target = target
        self.args = args

    def run(self):
        self.target.start(*self.args)

    def stop_task(self):
        self.target.die()

使用此类,我们将传递一个MyTask对象作为目标,并且将从start中调用MyThread.run方法。现在我们可以使用MyThread.stop_task停止在此线程上运行的任务。

o = MyTask()
t = MyThread(target=o)
t.start()
t.stop_task()
time.sleep(1.1)
print(t.is_alive())

请注意,我正在等待1.1秒,以测试线程是否还活着。这是因为目标(MyTask.start)最多需要一秒钟才能停止。该方法不会终止线程,而是调用MyTask.die并等待任务完成。如果要立即结束任务(并释放该任务使用的所有资源),可以使用Process并以.terminate结尾。如果您的任务执行的CPU操作多于IO操作,则还应该选择多处理而不是多线程,因为进程不受GIL的限制。


在重新设计源代码时,我注意到.die()调用sys.exit,所以我们不能用它来终止任务,因为它将停止程序。似乎这样做的原因是.start()调用了父对象的.start(),然后又调用了Reactor对象的.process_forever()方法。此方法开始在无中断条件的无限循环中运行Reactor.process_once()

可能的解决方案是子类SingleServerIRCBot并使用布尔变量来打破循环。此类应覆盖.start().die(),以阻止bot在线程上运行。 .die()方法会将标志设置为false,而.start()将循环调用Reactor.process_once()

import irc.bot

class MyBot(irc.bot.SingleServerIRCBot):
    def __init__(self, channel, nickname, server, port=6667):
        super(MyBot, self).__init__([(server, port)], nickname, nickname)
        self.channel = channel
        self._active = True

    def start(self):
        self._connect()
        while self._active:
            self.reactor.process_once(timeout=0.2)

    def die(self, msg="Bye, cruel world!"):
        self.connection.disconnect(msg)
        self._active = False

现在,我们可以通过在运行机器人的线程上调用.stop_task()或直接调用机器人的.die()方法来停止机器人。

host, port = 'irc.freenode.net', 6667
nick = 'My-Bot'
channel = '#python'

bot = MyBot(channel, nick, host, port)
t = MyThread(bot)
t.start()
t.stop_task()
#bot.die()