2014-02-06 4 views
0

Перед тем, как читать, знать, что я новичок в Python и очень новый для нарезания резьбы так что простите меня, если я неправильно понять, как работают темы или сделать ошибку новобранца: PPython Threading - Управление завершения нити и основной поток

Краткое описание моей цели:

  1. основной поток (а), («! начинают» например, отпечатки) некоторые вещи
  2. основной поток создается новый поток (б) сначала печатает «Thread B начал ", а затем печатает x + 1 навсегда (1, 2, 3 ...)
  3. Главная тема печатает "Woop!"
  4. Затем конец основного потока достигается, он завершает себя и затем переключается на нить б сделать б основной нити
  5. Программа теперь работает нитевую как основной поток так просто печать х + 1 навсегда и было забыто и уже не соответствующих
  6. Ctrl + C прекращает нить Ь и эффективно, вся программа будет прекращена, потому что поток а больше не существует

Вот что я так далеко (основы):

import threading, time 

def printCount(): 
    print "Thread B started" 
    x = 0 
    while True: 
     time.sleep(1) 
     x = x + 1 
     print x 

## User Code ## 
print "begin!" 
threadB = threading.Thread(target=printCount) 
threadB.start() 
print "woop!" 

Требования:

  • Я не хочу, чтобы изменить ниже отметки «Код пользователя» много на всех. Я , конечно, не хочу, чтобы обернуть его в классе, функции или собственный нить
  • Нажатие Ctrl + C в любой момент должен прекратить всю программу без каких-либо нитей слева работает (используя что-то вроде: except
    KeyboardInterrupt: os._exit(1))
    внутри пользователя код прекрасно
  • нитяный может продолжать работать вместо того, чтобы нить б основного нити, но в данном случае я не хочу код для обработки Ctrl + C завершения всей программы внутри кода пользователя раздел

Этот раздел пример не моя фактическая цель, просто упрощенная версия проблемы, которую я имею. Я пытаюсь создать инфраструктуру IRC, где пользователь может импортировать ее и использовать API очень просто, не загружая свой собственный код с помощью потоков и прерываний и т. П. Вот почему для кода пользователя важно быть максимально чистым.

Структура позволит пользователю создавать IRC-бот, который работает вечно, слушая команды, позволяя пользователю добавлять свои собственные команды. Ссылка Github - here, если вам интересно (это очень WIP atm).

ответ

1

Вы не можете «переключать» потоки. Поэтому, как только вы закончите свой основной поток, вам нужно подождать, пока другие потоки прекратятся, используя метод join. Но заметьте:

  • Поскольку метод join не прерываемый с KeyboardInterrupt, необходимо указать тайм-аут и петлю на обнаруживать прерывания пользователя.
  • Поскольку вы не можете заставить поток прекратить, вы должны реализовать стоп, используя threading.Event Механизм, например
  • Вы также должны использовать threading.Lock для предотвращения одновременного доступа по общим ресурсам, как sys.stdout (используется при печати)

Я собрал эти аспекты в классе под названием ThreadHandler, пожалуйста, посмотрите:

import threading, time 

def printCount(lock, stop): 
    with lock: 
     print "Thread B started" 
    x = 0 
    while not stop.is_set(): 
     time.sleep(1) 
     x = x + 1 
     with lock: 
      print x 

class ThreadHandler(): 
    STEP = 0.2 

    def __init__(self, target): 
     self.lock = threading.Lock() 
     self.stop = threading.Event() 
     args = (self.lock, self.stop) 
     self.thread = threading.Thread(target=target, args=args) 

    def start(self): 
     self.thread.start() 

    def join(self): 
     while self.thread.is_alive(): 
      try: 
       self.thread.join(self.STEP) 
      except KeyboardInterrupt: 
       self.stop.set() 

## User Code ## 
print "begin!" 
handler = ThreadHandler(target=printCount) 
handler.start() 
with handler.lock: 
    print "woop!" 
handler.join() 
1

написал короткую записку на другой вопрос, вчера аналогичные проблемы, это проверка можно реализовать в подтему «Ъ»:

Вместо while 1: сделайте следующее:

def printCount(): 
    main = None 
    for t in threading.enumerate(): 
     if t.name == 'MainThread': 
      main = t 
    print "Thread B started" 
    x = 0 
    while main and main.isAlive(): 
     time.sleep(1) 
     x = x + 1 
     print x 

Это было быть хорошей идеей для хранения main в глобальной области для всех потоков, использующих isntead для того, чтобы искать основной поток для каждой инициализации подпотока. Но это сделало бы работу в вашем примере.

main будет ручкой к вашему основному потоку перебирая все нити (.enumerate()), а затем поместить нить под названием «MainThread» в main и затем вызвать main.isAlive(), чтобы проверить, если он все еще работает. если main является None или False или если .isAlive() возвращает False это будет означать, что поток либо несуществующий или мертвый, выключая свою подтему :)

+1

Я хочу, чтобы нить, содержащую PrintCount продолжать работать вечно, но это в том esting! – Jazcash

+0

@Jazcash О, мой плохой, думал, что я читал, что вы хотите, чтобы потоки НЕ запускались навсегда :) – Torxed

0

Вы не можете переключать потоки, как это. Это не работает.

Однако вы можете использовать сигналы с глобальным флагом ALIVE:

import threading, time, signal 

ALIVE = True 

def handle_sigint(signum, frame): 
    global ALIVE 
    ALIVE = False 

signal.signal(signal.SIGINT, handle_sigint) 

def printCount(): 
    print "Thread B started" 
    x = 0 
    while ALIVE: # <--- note the change 
     time.sleep(1) 
     x = x + 1 
     print x 

## User Code ## 
print "begin!" 
threadB = threading.Thread(target=printCount) 
threadB.start() 
print "woop!" 

signal.pause() # <--- wait for signals 

Теперь он будет корректно выйти после нажатия CTRL + C.

+0

Это хорошо, но тогда у вас есть signal.pause() в коде пользователя. Возможно, я должен был привести лучший пример. Есть ли способ запустить поток, как только основной поток закончится? – Jazcash

+1

@Jazcash Вы можете начать поток с любого потока. Однако вам нужен основной поток для обработки сигналов типа CTRL + C. Если основной поток завершается, вы не поймаете этот сигнал. – freakish

Смежные вопросы