Ваши потоки работают в бесконечном цикле и не знают когда им завершить работу. Когда очередь пустеет, то они “повисают” здесь
в ожидании новых задач. Вы можете в конце класть в очередь некие объекты-маркеры, получение которых будет для потока сигналом к остановке.
# Класс объекта-маркера
class JobDone(object):
pass
...
# После того как в очередь положили все host, создаем
# и кладем число объектов-маркеров по числу потоков
for i in range(60):
queue.put(JobDone())
...
# В коде потока проверяем что мы достали из очереди и если
# это объект-маркер завершения работы, то выходим из цикла
while True:
#grabs host from queue
host = self.queue.get()
if isinstance(host, JobDone):
break
ДОБАВЛЕНО: И забыл еще сказать, что выполнение задачи должно быть обернуто в try-finally. Поскольку если ваш поток совершит ошибку, то он завершится не отчитавшись о выполнении задачи. Это значит, что если в вашем коде вы сделаете queue.join(), которое приостановит выполнение основного потока до выполнения всех задач, то никогда не сможете выйти из этого ожидания. Поскольку поток задачу на обработку взял, но queue.task_done() так и не выполнил.
while True:
try:
#grabs host from queue
host = self.queue.get()
if isinstance(host, JobDone):
break
...
except Exception as e:
# При желании записываем сведения об ошибке в лог
finally:
queue.task_done()