Рассмотрим ситуацию, в которой потоки читают показаний нескольких датчиков одновременно. В случае возникновения ошибки ввода/вывода во время чтения показаний одного из датчиков или другого подобного сбоя встает необходимость перезапускать упавший поток.
Первое, что приходит в голову это просто проверить, жив ли поток, и перезапустить его, если он умер:
# как то так if not current_thread.isAlive(): current_thread.start()
Попробуем смоделировать ситуацию:
import threading, time def sensor_a(): while True: time.sleep(1) print("Читаем показания датчика А") def sensor_b(): i = 0 while True: time.sleep(1) if i == 1: # имитируем ошибку при чтении датчика raise KeyboardInterrupt print("Читаем показания датчика Б") i += 1 # словарь с потоки для чтения счетчиков # словарь необходим для их перезапуска thread_dict = { 'SENSOR_A': threading.Thread(target=sensor_a, name='SENSOR_A'), 'SENSOR_B': threading.Thread(target=sensor_b, name='SENSOR_B') } threads = [t for t in thread_dict.values()] # запускаем потоки for t in threads: t.start() # следим за потоками в реальном времени while True: # проходимся по объектам потоков for thread in threading.enumerate(): # если поток умер if not thread.isAlive(): print(f'Поток, читающий {thread.name} умер') # получаем из словаря `thread_dict` # поток по имени для его перезапуска restart = thread_dict[thread.name] # пытаемся перезапустить restart.start() # Читаем показания датчика А # Читаем показания датчика Б # Читаем показания датчика А # Exception in thread sensor_b: # Traceback (most recent call last): # ... # KeyboardInterrupt # Поток, читающий SENSOR_B умер # Перезапуск потока SENSOR_B # Traceback (most recent call last): # ... # RuntimeError: threads can only be started once # Читаем показания датчика А # Читаем показания датчика А # ...
Вот и ответ на поставленный вопрос "RuntimeError: threads can only be started once" - потоки могут быть запущены только один раз! Другими словами потоки перезапускать НЕЛЬЗЯ.
В такой ситуации правильней и проще вообще не позволять умирать потокам, ведь если потоки на самом деле рушатся, то следом может упасть и вся программа. Если в функции потока, какой-то блок кода может вызывает исключение, то лучше подстраховаться и просто ловить это исключение, а в качестве результата выдать например None
.
Если код программы уже написан и работает, а править очень долго, то можно поступить вообще тривиально, обернув функцию потока в другую функцию, которая будет ловить все исключения подряд.
Например:
import threading, time def sensor_a(): while True: time.sleep(1) print("Читаем показания датчика А") def sensor_b(): i = 0 while True: time.sleep(1) if i == 1: # имитируем падение датчика raise KeyboardInterrupt print("Читаем показания датчика Б") i += 1 def threadwrap(threadfunc): def wrapper(): while True: try: threadfunc() except BaseException as e: th_name = threading.current_thread().name print(f'Падение потока датчика {th_name}, перезапуск...') return wrapper # словарь потоков thread_dict = { 'SENSOR_A': threading.Thread(target=sensor_a, name='SENSOR_A'), 'SENSOR_B': threading.Thread(target=sensor_b, name='SENSOR_B') } # создаем потоки threads = [t for t in thread_dict.values()] # запускаем потоки for t in threads: t.start() # Читаем показания датчика Б # Читаем показания датчика А # Падение потока датчика SENSOR_B, перезапуск... # Читаем показания датчика А # Читаем показания датчика А # Читаем показания датчика Б # Читаем показания датчика А # ...