Вам подходит концепция очереди. Первая функция наполняет очередь, N потоков функций 2 получают задачу из очереди, если задач нет, ждут когда появится.
Но тут есть важный ньюанс, если вы за счет многопоточности хотите получить выигрышь в производительности в сложных для CPU задачах (все то что способно его максимально нагрузить ), то вы этого не добьетесь из за GIL (глобальной блокировки). В таком случае вам надо не много поточность, а многопроцессность. Т.е. когда порождаются не потоки, а процессы.
За многопоточность отвечает модуль threading, за многопроцессность multiprocessing. В зависимости от выбора существует разные очередию Для первого случая есть отдельный модуль Queue
(Вот тут на него можно посомтреть с примерами). Для второго случая в самом модуле есть Queue класс или пакет.
Так же можно посмотреть в сторону Celery, в случае если вам надо что то наподобие сервиса который должен работать всегда