In partnership with

A process is a program in execution; It’s like a factory with resources.

Threads are workers executing the code.

Today, you will learn how to control multiple threads.

But first, why?

I don’t know if you’ve played Overcooked with friends.

One essential step is chopping the ingredients.

When your friend is chopping an onion…

Do you wait for him to finish?

No, you go next to him and chop something else.

That’s the same in programming.

Doing things simultaneously is usually more efficient.

The formal word for this is parallelism.

There are two types:

  1. Parallelism: using multiple CPUs to run tasks at the same time. (multiprocessing, Python threading without GIL)

  2. Concurrency: rapid task-switching to create the illusion of parallelism because you don't have access to multiple CPUs (Python with GIL, asyncio)

Now you understand the big picture, let’s see how to use them to make your code runs faster.

Creating threads

By default, Python runs in the main thread:

print("in the main thread")
print("hello")

To create a new thread, use threading:

It runs concurrently with the main thread.

How do we prove that?

Add a few print statements and look at the output:

import time
import threading

def helloWorld(name):
    print("before")
    time.sleep(1)
    print(f"thread {name}: hello world")

thread1 = threading.Thread(target=helloWorld, args=(1,))

thread1.start()
print("end")

Notice how “end” runs before “thread1: hello world?”

before thread start
end
thread1: hello world

What happens is:

  1. thread1.start() starts the thread and immediately returns

  2. The main thread continues and prints "end"

  3. Meanwhile, thread1 is running concurrently and will print "before"

Thread.join()

Most of the time, you want a result from a thread.

A network request is meaningless if you can't read the response.

But how do you make sure the thread is done before you use its returned result?

This is where you need thread.join()

It blocks the main thread and waits for the new thread to finish:

import time
import threading

def helloWorld(name):
    print("before")
    time.sleep(1)
    print(f"thread {name}: hello world")

thread1 = threading.Thread(target=helloWorld, args=(1,))

thread1.start()
thread1.join() #blocking
print("end")

Output:

before thread start
thread1: hello world
end

Controlling multiple threads

Now it’s time to unleash the true power of multithreading.

Imagine you are crawling three different websites.

You don't care which one finished first.

You just want to make sure that all of them are done before moving on.

Here’s the code:

This approach is much more efficient than crawling the links one by one.

This pattern is so common that Python gives you a simpler way to do it:

with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    executor.map(crawl, links)

This creates three threads that run in parallel.

Then automatically calls start and join for all the threads.

The Future of Tech. One Daily News Briefing.

AI is moving faster than any other technology cycle in history. New models. New tools. New claims. New noise.

Most people feel like they’re behind. But the people that don’t, aren’t smarter. They’re just better informed.

Forward Future is a daily news briefing for people who want clarity, not hype. In one concise newsletter each day, you’ll get the most important AI and tech developments, learn why they matter, and what they signal about what’s coming next.

We cover real product launches, model updates, policy shifts, and industry moves shaping how AI actually gets built, adopted, and regulated. Written for operators, builders, leaders, and anyone who wants to sound sharp when AI comes up in the meeting.

It takes about five minutes to read, but the edge lasts all day.

Fee from Anime Coders

How did you like today's email?

Login or Subscribe to participate

Keep Reading

No posts found