Я не думаю, что вы могли (или должны) убить поток напрямую, но вы могли бы остановить задачу, выполняющуюся в этом потоке. Тогда поток будет неактивным, и вы можете удалить его из списка потоков, если хотите. Я не знаком с 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
. Вы также должны выбрать многопроцессорность вместо многопоточности, если ваша задача выполняет больше операций с ЦП, чем операций ввода-вывода, потому что процессы не ограничены GIL .
После изучения исходного кода, я заметил, что .die()
вызывает sys.exit
, поэтому мы не можем использовать его для завершения задачи, потому что это остановит программу. Кажется, причина этого в том, что .start()
вызывает родительский объект .start()
, который затем вызывает .process_forever()
метод Reactor
объект. Этот метод запускает Reactor.process_once()
в бесконечном цикле без условия разрыва.
Возможное решение - создать подкласс SingleServerIRCBot
и использовать логическое значение variabe для разрыва цикла. Этот класс должен переопределить .start()
и .die()
, чтобы остановить работу бота в потоке. Метод .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()