You want a thread pool.
The idea of a thread pool, is that instead of creating new threads, your application code creates new tasks, and then it submits the tasks to the thread pool. The pool consists of some number of threads (maybe a variable number, maybe a fixed number, depends how its implemented) and a blocking queue.
The client program puts tasks onto the blocking queue, while each of the pool threads sits in a loop, taking tasks from the queue and performing them.
A simplistic thread pool might have a fixed set of threads, and it might run forever. A sophisticated thread pool might have means to spin up or shut down threads in response to changing demand and/or changing system load.
I don't have enough Python experience to know whether there is a standard thread pool interface that everybody uses or, to recommend any existing thread pool implementation.
You can always write your own. A thread pool with eight fixed threads that run forever would not be hard to make.