Делай раз:
- каждый поток должен быть завершен, т.е. th1.join() не напрасно придуман. В этом примере не нужно, но правило хорошее.
Делай два:
- если приложение закрывается - нужно сказать всем потокам о завершении. Флажочек выставить в крайнем случае. Пусть закроются не сразу, а после того как сделают твой re.findall например.
Делай три:
- когда в главном потоке поймал KeyboardInterrupt (SystemExit или что там еще хочется) - заявляй о прекращении банкета.
Есть еще вариант - неправильный.
Сказать th1.setDaemon(True)
Но тогда поток завершиться мгновенно, и можно легко поиметь порванные данные.
Я советую как лучше, а там уж тебе решать, что ближе: надежность или “авось пронесет”.
Многие выбирают второй пункт, как не прискорбно.
P.S. Хороший пример. Можно понять, что хотелось - и нет лишнего. Лучший - тот, который можно воспроизвести. Качнуть
google.com и попытаться найти там какой-нибудь случайный символ, к примеру.