Каков "правильный" способ сделать поток stoppable в Python, учитывая остановленные псевдоатомные единицы работы?

Я пишу программу на Python. Эта программа прерывается очень часто, взаимодействием пользователя (CRTL + C), а также другими программами, отправляющими различные сигналы, все из которых должны останавливать работу потока различными способами. Нить выполняет кучу единиц работы (я называю их "атомами" ) в последовательности.

Каждый атом может быть остановлен быстро и безопасно, поэтому сделать сам поток остановки довольно тривиальным, но мой вопрос: что такое "правильный", или канонический способ реализации потока с остановкой, заданный stoppable, pseudo -атомные работы должны быть выполнены?

Должен ли я опросить флаг stop_at_next_check перед каждым атомом (пример ниже)? Должен ли я украсить каждый атом чем-то, что делает проверку флага (в основном то же, что и в примере, но скрыто в декораторе)? Или я должен использовать другую технику, о которой я не думал?

Пример (простая проверка остановленного флага):

class stoppable(Thread):
    stop_at_next_check = False
    current_atom = None

    def __init__(self):
        Thread.__init__(self)

    def do_atom(self, atom):
        if self.stop_at_next_check:
            return False
        self.current_atom = atom
        self.current_atom.do_work()
        return True     

    def run(self):
        #get "work to be done" objects atom1, atom2, etc. from somewhere

        if not do_atom(atom1):
            return

        if not do_atom(atom2):
            return

        #...etc

    def die(self):
        self.stop_at_next_check = True
        self.current_atom.stop()

Ответ 1

Проверка флагов кажется правильной, но вы упустили возможность упростить ее, используя список для атомов. Если вы помещаете атомы в список, вы можете использовать одиночный цикл, не нуждаясь в методе do_atom(), и проблема того, где делать проверку, решает сам.

def run(self):
    atoms = # get atoms
    for atom in atoms:
        if self.stop_at_next_check:
             break
        self.current_atom = atom
        atom.do_work()

Ответ 2

Создайте флаг "thread x should continue processing", и когда вы закончите с потоком, установите флаг в false.

Убийство потока напрямую считается плохим, потому что вы можете получить дробную часть работы.

Ответ 3

Чуть позже, но я создал небольшую библиотеку, муравьи, для решения этой проблемы. В вашем примере атомная единица представлена рабочим

пример

from ants import worker

@worker
def hello():
    print("hello world")

t = hello.start()
...
t.stop()

В приведенном выше примере hello() будет запускаться в отдельном потоке, вызываемом через цикл while True: тем самым выпуская "мир приветствия" как можно быстрее.

Вы также можете иметь триггерные события, например, выше, замените hello.start() на hello.start(lambda: time.sleep(5)) и вы будете запускать его каждые 5: секунду

Библиотека очень новая и работа над GitHub продолжается https://github.com/fa1k3n/ants.git

Будущая работа включает в себя добавление colony для нескольких работников, работающих над разными частями одних и тех же данных, а также планирование queen для общения и контроля работников, таких как синхронизация