In realtà in python non funziona proprio così perché per colpa del GIL il multithreading non può essere parallelo: se hai due threads, l'interprete esegue un po' del codice del primo thread, poi lo blocca ed esegue un po' del codice del secondo thread e così via, ma non viene mai eseguito più di un thread per volta. Se vuoi sfruttare più di un core sei costretto ad usare il multiprocessing che però ha più overheads rispetto al multithreading. Il multithreading di python è un multithreading finto, ma anche il multiprocessing di python è un multiprocessing finto perché non spawna necessariamente un nuovo processo. Non è un linguaggio pensato per essere performante e anche quando si usano questi strumenti non si va tanto lontano.Il problema di avviare un nuovo thread per ogni riga della wordlist è che diventerà inefficiente appena il numero di righe supera il numero di core del processore fisico (andranno in time-sharing e avviare un thread è un operazione dispendiosa). L'approccio che sfrutta meglio i thread sarebbe dividere la wordlist per il numero di core, avviare tanti thread quanti core ci sono e dare una frazione diversa della wordlist a ciascuno.
Nel caso di @k0Br@3390 il multithreading va bene perché in ogni caso il bottleneck di questo bruteforce è sicuramente l'I/O dalla rete che è lentissimo rispetto al resto.