It's usually easier imo to separate them into different processes, perhaps running on different machines, and communicate via some form of message passing (I like Rabbitmq).
For example, at my company, we have a web application that does some batch processing, so we have two separate services, our web server and our batch processing service. The web server handles requests, does database calls, etc, and if there's a larger operation, it sends a message on Rabbitmq for our batch processor consumer to handle. That consumer handles some number of threads and schedules work as needed.
This gives us a few benefits:
- web server can handle I/o heavy tasks, which means either lots of threads or an async loop
- consumer can handle CPU heavy tasks, which means matching threads to CPU cores
If you host them on the same machine, you can pin the CPU heavy threads to certain cores to minimize context switching costs, and use the rest for switching between the web threads.
I find this works pretty well and it's pretty easy to scale up for production. I hope this helps.