A somewhat clumsy ascii-art to demonstrate the mechanism: The join() is presumably called by the main-thread. It could also be called by another thread, but would needlessly complicate the diagram.

join-calling should be placed in the track of the main-thread, but to express thread-relation and keep it as simple as possible, I choose to place it in the child-thread instead.

    without join:
    +---+---+------------------                     main-thread
        |   |
        |   +...........                            child-thread(short)
        +..................................         child-thread(long)
    
    with join
    +---+---+------------------***********+###      main-thread
        |   |                             |
        |   +...........join()            |         child-thread(short)
        +......................join()......         child-thread(long)

    with join and daemon thread
    +-+--+---+------------------***********+###     parent-thread
      |  |   |                             |
      |  |   +...........join()            |        child-thread(short)
      |  +......................join()......        child-thread(long)
      +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,     child-thread(long + daemonized)

    '-' main-thread/parent-thread/main-program execution
    '.' child-thread execution
    '#' optional parent-thread execution after join()-blocked parent-thread could 
        continue
    '*' main-thread 'sleeping' in join-method, waiting for child-thread to finish
    ',' daemonized thread - 'ignores' lifetime of other threads;
        terminates when main-programs exits; is normally meant for 
        join-independent tasks

So the reason you don't see any changes is because your main-thread does nothing after your join. You could say join is (only) relevant for the execution-flow of the main-thread.

If, for example, you want to concurrently download a bunch of pages to concatenate them into a single large page, you may start concurrent downloads using threads, but need to wait until the last page/thread is finished before you start assembling a single page out of many. That's when you use join().

Answer from Don Question on Stack Overflow
Top answer
1 of 12
424

A somewhat clumsy ascii-art to demonstrate the mechanism: The join() is presumably called by the main-thread. It could also be called by another thread, but would needlessly complicate the diagram.

join-calling should be placed in the track of the main-thread, but to express thread-relation and keep it as simple as possible, I choose to place it in the child-thread instead.

    without join:
    +---+---+------------------                     main-thread
        |   |
        |   +...........                            child-thread(short)
        +..................................         child-thread(long)
    
    with join
    +---+---+------------------***********+###      main-thread
        |   |                             |
        |   +...........join()            |         child-thread(short)
        +......................join()......         child-thread(long)

    with join and daemon thread
    +-+--+---+------------------***********+###     parent-thread
      |  |   |                             |
      |  |   +...........join()            |        child-thread(short)
      |  +......................join()......        child-thread(long)
      +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,     child-thread(long + daemonized)

    '-' main-thread/parent-thread/main-program execution
    '.' child-thread execution
    '#' optional parent-thread execution after join()-blocked parent-thread could 
        continue
    '*' main-thread 'sleeping' in join-method, waiting for child-thread to finish
    ',' daemonized thread - 'ignores' lifetime of other threads;
        terminates when main-programs exits; is normally meant for 
        join-independent tasks

So the reason you don't see any changes is because your main-thread does nothing after your join. You could say join is (only) relevant for the execution-flow of the main-thread.

If, for example, you want to concurrently download a bunch of pages to concatenate them into a single large page, you may start concurrent downloads using threads, but need to wait until the last page/thread is finished before you start assembling a single page out of many. That's when you use join().

2 of 12
101

Straight from the docs

join([timeout]) Wait until the thread terminates. This blocks the calling thread until the thread whose join() method is called terminates – either normally or through an unhandled exception – or until the optional timeout occurs.

This means that the main thread which spawns t and d, waits for t to finish until it finishes.

Depending on the logic your program employs, you may want to wait until a thread finishes before your main thread continues.

Also from the docs:

A thread can be flagged as a “daemon thread”. The significance of this flag is that the entire Python program exits when only daemon threads are left.

A simple example, say we have this:

def non_daemon():
    time.sleep(5)
    print 'Test non-daemon'

t = threading.Thread(name='non-daemon', target=non_daemon)

t.start()

Which finishes with:

print 'Test one'
t.join()
print 'Test two'

This will output:

Test one
Test non-daemon
Test two

Here the master thread explicitly waits for the t thread to finish until it calls print the second time.

Alternatively if we had this:

print 'Test one'
print 'Test two'
t.join()

We'll get this output:

Test one
Test two
Test non-daemon

Here we do our job in the main thread and then we wait for the t thread to finish. In this case we might even remove the explicit joining t.join() and the program will implicitly wait for t to finish.

🌐
Python
docs.python.org › 3 › library › threading.html
threading — Thread-based parallelism — Python 3.14.4 ...
When the timeout argument is present and not None, it should be a floating-point number specifying a timeout for the operation in seconds (or fractions thereof). As join() always returns None, you must call is_alive() after join() to decide whether a timeout happened – if the thread is still alive, the join() call timed out.
🌐
Codecademy
codecademy.com › docs › python › threading › .join()
Python | Threading | .join() | Codecademy
June 3, 2022 - The .join() method delays a program’s flow of execution until the target thread has been completely read. ... Looking for an introduction to the theory behind programming? Master Python while learning data structures, algorithms, and more!
🌐
Tutorialspoint
tutorialspoint.com › python › python_joining_threads.htm
Python - Joining the Threads
In Python, joining the threads means using the join() method to wait for one thread to finish before moving on to others. This is useful in multithreaded programming to make sure some threads are completed before starting or continuing with ...
🌐
Mimo
mimo.org › glossary › python › multithreading
Mimo: The coding platform you need to learn Web Development, Python, and more.
import threading import time def task(name): print(f"Thread {name}: starting...") time.sleep(2) # Simulate an I/O operation like a download print(f"Thread {name}: finishing.") # Create and start the thread my_thread = threading.Thread(target=task, args=("A",)) my_thread.start() print("Main thread: doing other work.") # Wait for the thread to finish before the program exits my_thread.join() print("Main thread: all done.") Due to Python's Global Interpreter Lock (GIL), multithreading will not speed up tasks that are heavy on CPU calculations.
🌐
Reddit
reddit.com › r/learnprogramming › python problems with threading library, print function and thread.join() method
r/learnprogramming on Reddit: python problems with threading library, print function and Thread.join() method
January 21, 2023 -

i hope you guys can help me because has been two days that i on this thing. probably i'm stupid, but i can't get this. so, i'm studying this piece of code (taken from this video: https://youtu.be/StmNWzHbQJU) and i just modified the function called by Threading class.

import threading
    from time import sleep
    from random import choice
    
    def doThing():
        threadId = choice([i for i in range(1000)]) # just 'names' a thread
        while True:
            print(f"{threadId} ", flush=True)
            sleep(3)
            
    threads = []
    
    for i in range(50):
        t = threading.Thread(target=doThing, daemon = False)
        threads.append(t)
    
    for i in range(50):
        threads[i].start()
    
    for i in range(50):
        threads[i].join()

the problems are basically 3:

  1. i can't stop the program with ctrl+c like he does in the video. i tried by set daemon = False or delet the .join() loop, nothing work, neither in the Idle interpeter neithe in the command line and powershell (i'm on windows);

  2. as i said,i tried to set daemon=False and to delete the .join() loop, but nothing change during the execution so i'm a little bit confused on what "daemon" and ".join()" actually does;

  3. the function doThing() is endless so the join() shouldn't be useful. And i don't understand why there are two "for" loops, one for start() and one for join(). Can't they be into the same "for" cycle?

  4. last thing, the print output is totally different between Idle and powershell: in Idle i get some lines with different numbers, in the powershell i get only one number per line (look at the images):https://ibb.co/HtMr9gf, https://ibb.co/Y8gzDtw, but in visual code, which use powershell too, i get this: https://ibb.co/X82vY3v

can you help me to understand this please? i'm really confused. thank you a lot

Top answer
1 of 2
2
Multithreading is a long and complicated topic. It is very very difficult, and multithreading bugs can stump even the most experienced developers. I will try to summarize some stuff, hope it helps your understanding, but it is out of scope to try to explain everything. So start() tells the other thread to start running INDEPENDENTLY of the current thread. That is, the current thread moves to execute the next line without waiting for the other thread. join() is the opposite of start(). You could also think of it as "wait()". The current thread will halt execution until the function being run by the other thread terminates. Usually, every start() call should have a corresponding join() call. Otherwise, if the main thread terminates before all of the other child threads, then the child threads will keep on running with no main thread to actually utilize the work done. (In some other languages main thread finishing before other threads will immediately crash the program). Therefore most simple multithreading programs have this pattern: threads = SomehowMakeThreads(50) # start all threads for i in range(50): threads[i].start() # Main thread can also # do work while other threads run DoMyOwnWork() # wait for thread 1. # then wait for thread 2 etc. # takes O(max time of longest thread to run) for i in range(50): threads[i].join() ProcessAndCleanup() You asked why not do this? for i in range(50): threads[i].start() threads[i].join() Well, think about what this means... You tell thread 0 to start, but then immediately wait for it to finish. After thread 0 finished, THEN you ask thread 1 to start etc. This will take the sum of the times each thread runs. Performance is the same as single threading. If you instead start all the threads at once, and then wait for them to all finish, the runtime should be around the max time for a single thread to finish. You can save a lot of time this way! --- Python keeps running if there are any non-daemon threads still running. By default, threads have daemon = False. Setting daemon = True means Python will not wait for this thread to finish if all non-daemon threads are done. Source: https://docs.python.org/3/library/threading.html But even then, afaik on windows ctrl-c does not work to interrupt a thread that's waiting on join(). I do not know if it was ever fixed. https://mail.python.org/pipermail/python-dev/2017-August/148800.html . This link gives some workarounds https://stackoverflow.com/a/52941752/17786559 . I think it does work on linux tho. --- Lastly regarding why you keep seeing different printed output, this is the hardest part of multithreading known as race conditions. Lets say you have thread 1 printing "aaaa" while thread 2 AT THE SAME TIME prints "bbbb". What actually gets printed to console? Is it "aaaabbbb" or "bbbbaaaa" or "abababab" or some other permutation? The answer is every time you run it you will see something different. There are 0 guarantees in terms of execution ordering across threads. It is completely and utterly random. This is called a "race condition" Programs/functions have to be carefully written using special techniques to handle race conditions - programs which do so are called "thread-safe". print() is not thread safe. Use the logging module instead which is thread-safe (order of lines printed may still be random though). With multithreading it's actually recommended not to use any printing for debugging, since even the act of printing to console can alter the thread timings. Though for learning purposes it's ok for a beginner. Concurrency is random by nature so do not expect your program to do the same thing each time. It's partly why multithreading is so hard yet so interesting!
2 of 2
1
For point 1 I do not know, maybe it is a difference between linux and windows? not sure. Point 2: About the join(), the answer is actually on the point 3, as you said doThing() is endless so it doesn't practically matter to have a join call Point 3: the join call is a so-called blocking function, try to put them in the same loop and see what happens Point 4: I think it depends on how various terminal flush the output, totally normal with multithreaded applications
🌐
GeeksforGeeks
geeksforgeeks.org › python › joining-threads-in-python
Joining Threads in Python - GeeksforGeeks
June 22, 2020 - The global variables (stored in the heap) and the program codes are shared among all the threads. On invoking the join() method, the calling thread gets blocked until the thread object (on which the thread is called) gets terminated.
Find elsewhere
🌐
Bogotobogo
bogotobogo.com › python › Multithread › python_multithreading_Daemon_join_method_threads.php
Python Multithreading Tutorial: daemon threads & join method - 2020
In our sample, join() blocks the calling thread (main thread) until the threads (d / t) whose join() method is called is terminated - either normally or through an unhandled exception - or until the optional timeout occurs.
🌐
Real Python
realpython.com › intro-to-python-threading
An Intro to Threading in Python – Real Python
August 5, 2024 - To tell one thread to wait for another thread to finish, you call .join().
🌐
Super Fast Python
superfastpython.com › home › tutorials › how to join a thread in python
How to Join a Thread in Python - Super Fast Python
September 11, 2022 - You can join a thread by calling the Thread.join() function. In this tutorial you will discover how to join threads in Python. Let’s get started. Need to Join a Thread A thread is a thread of execution in a computer program.
🌐
Instructables
instructables.com › circuits › raspberry pi
How to Create a Thread in Python Using Threading.thread() Module and Pass Data Arguments to Thread : 5 Steps - Instructables
February 27, 2024 - How to Create a Thread in Python Using Threading.thread() Module and Pass Data Arguments to Thread: Raspberry Pi, a small and affordable single-board computer, has gained immense popularity for its versatility and ease of use. It is widely used for building home automation system, robots and ...
🌐
IncludeHelp
includehelp.com › python › thread-join-method-with-example.aspx
Python Thread join() Method with Example
April 24, 2023 - In the initial three Thread objects, we first created a thread and waited for it to execute and then joined to the main thread. So they are printed in the same order as called. In the next three Thread objects, they are running simultaneously, so they get printed according to the time they should be executed, ... # Python program to explain the # use of join() method in Thread class # with timeout parameter defined import time import threading def thread_1(i): time.sleep(2) print('Value by Thread 1:', i) def thread_2(i): time.sleep(5) print('Value by Thread 2:', i) # Creating three sample threads thread1 = threading.Thread(target=thread_1, args=(1,)) thread2 = threading.Thread(target=thread_2, args=(2,)) # Running three thread object thread1.start() thread1.join(timeout=5) thread2.start() thread2.join()
🌐
Oregoom
oregoom.com › home › joining threads in python
▷ Joining Threads in Python - Oregoom.com
October 29, 2024 - You can join multiple threads in Python using a for loop to call join() on each thread.
🌐
Medium
medium.com › @e.ahmadi › when-and-why-we-should-use-join-in-multi-threading-5e7fa331c05d
When and why we should use Join in multi-threading | by Ehsan Ahmadi | Medium
September 3, 2021 - in order to create a thread in Python we can use “threading” library, For instance: ... thread start thread finished process exitedExecuting - /Users/Ehsan/test_threading1.py Execution Starts: 0.874791534 Executions Ends: 10.883360908 Totals Execution Time:10.01 seconds. We can imagine the new thread (t1) is forked off the main thread and will be doing a job in 10 seconds. The main question is why should we use the “join” function in the main function?
🌐
Pythontic
pythontic.com › multithreading › thread › join
Python Thread - join() method | Pythontic.com
Join method in Python makes the calling thread to wait for another thread. The wait could be till another thread terminates or based on a timeout value.
🌐
Real Python
realpython.com › lessons › joining-threads
Joining Threads (Video) – Real Python
This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas. ... In this lesson, you’ll learn how to use the join() method to bring all your threads together before the main thread exits.
Published   November 19, 2019
🌐
Delft Stack
delftstack.com › home › howto › python › join threads in python
How to Join Threads in Python | Delft Stack
March 11, 2025 - This tutorial demonstrates how to use the join method with threads in Python. Learn the significance of the join method, how to manage multiple threads, and handle exceptions effectively. Enhance your Python programming skills with practical examples and clear explanations.
🌐
Dkharazi
dkharazi.github.io › notes › py › threading › join
t.join
>>> t = Thread(target=sleepy) >>> t.start() thread started >>> t.join() # haults any further execution thread done # until thread finishes >>> print('main done') # now we can print main done · Python Essential References · Documentation for Threading API ·
🌐
StrataScratch
stratascratch.com › blog › python-threading-like-a-pro
Python Threading Like a Pro - StrataScratch
September 6, 2023 - Now comes stopping the Python thread. The join() method makes the main thread wait for the t thread to complete its execution, but only up to 1 second, which is defined in the timeout parameter.
🌐
DataFlair
data-flair.training › blogs › python-program-on-join-method-in-thread
Python Program on Join Method in Thread - DataFlair
April 23, 2024 - The explicit join method involves explicitly calling the ‘join’ method on a thread within the main program. This action signals the main program to pause and wait for the associated thread to finish its execution before continuing. This method provides a level of control, allowing programmers to manage the sequence of thread execution explicitly.