import threading
import time
import random


def do_shit() -> None:
    '''Эту функцию будет вызывать другая - "сигнальная" функция'''
    print(f'Shit called at {time.perf_counter()-START}')
    print(f'Shit got: {random.randint(1,1000)}')  # имитируем какую-то херню


def signal_loop() -> None:
    '''Функция должна сигналить раз в 3 секунды, запускается в отдельном Thread'''
    print(f'signal_loop started at {time.perf_counter() - START}')
    while True:
        t = threading.Thread(target=do_shit, daemon=True)
        t.run()
        time.sleep(3)
    print(f'signal_loop ended at {time.perf_counter() - START}')  # по идее это не должно вызваться никогда


def find_prime(maxx: int) -> None:
    '''Занимающая CPU процедура'''
    print(f'find_prime started at {time.perf_counter() - START}')
    primes = []
    for num in range(3, int(maxx)+1, 2):  # типо ищем простые числа
        for i in range(3, int(num**0.5)+1, 2):
            if num % i == 0:
                break
        else:
            primes.append(num)
    print(f'find_prime ended at {time.perf_counter() - START}')


def main() -> None:
    '''Отсюда вызываем 2 другие функции - сигнальную и занимающую CPU'''
    t1 = threading.Thread(target=find_prime, args=[1e6])
    t2 = threading.Thread(target=signal_loop, daemon=True)  # когда закончится 1й тред, эта хрень тоже должна кончиться
    t1.start()
    t2.start()


if __name__ == '__main__':
    print('Starting main thread')
    START = time.perf_counter()  # относительно START считаем прошедшее время
    main()
    print(f'Main thread ended at {time.perf_counter() - START}')