Thread Limits . .

This is true at a basic level, but as always, it’s never quite that simple. There are a number of CPU-bound tasks which can actually be parallelized over multiple Python threads; the only requirement is that the heavy lifting is done by a C (or Fortran) extension that first releases the GIL. Conversely, even with I/O-bound tasks, there will usually be a limit on the number of threads that you can usefully run, due to the overhead of context switching - though it’s far FAR greater than your CPU core count. So when you truly need epic numbers of connections (say, a high-volume web app), you’ll need to go for some sort of asynchronous I/O event loop (see eg the asyncio module), and possibly even multiple such event loops in order to take advantage of your CPU cores.

Here’s a simple and rather stupid example of a program that can happily use all your CPU cores with a single process:

import bcrypt
import threading

# Tune these to your CPU's capabilities
COST = 10
THREADS = 16

pin = input("Enter a four-digit PIN: ")
password = bcrypt.hashpw(pin.encode(), bcrypt.gensalt(COST))
pin = None # Pretend we can't see this one

# Okay, now to brute-force that thing!
tasks = iter(range(10000))
found = None
def search():
	global found
	for task in tasks:
		if bcrypt.checkpw(b"%04d" % task, password):
			found = task
		if found is not None:
			break

threads = [threading.Thread(target=search) for _ in range(THREADS)]
for t in threads: t.start()
for t in threads: t.join()
if found is None:
	print("I guess that wasn't a four-digit PIN.")
else:
	print("The PIN you entered was:", found)

(It’s worth noting that a bcrypt cost factor of 10 rounds is pretty poor by today’s standards, but I didn’t feel like cooking my CPU for hours just for a quick demo.)

Even with the GIL, this is entirely capable of saturating a full set of cores, simply because most of the work isn’t happening in Python. This is a more general case of “I/O bound works well with threads”, since I/O also isn’t happening in Python.

And yes, this is CPython (specifically version 3.12, although probably any version will work).