What is the use of join() in Python threading?
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
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
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 oneTest non-daemonTest two
Here the master thread explicitly waits for the
t thread to finish until it calls
Alternatively if we had this:
print 'Test one'print 'Test two't.join()
We'll get this output:
Test oneTest twoTest 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.
Thanks for this thread -- it helped me a lot too.
I learned something about .join() today.
These threads run in parallel:
and these run sequentially (not what I wanted):
In particular, I was trying to clever and tidy:
class Kiki(threading.Thread): def __init__(self, time): super(Kiki, self).__init__() self.time = time self.start() self.join()
This works! But it runs sequentially. I can put the self.start() in __ init __, but not the self.join(). That has to be done after every thread has been started.
join() is what causes the main thread to wait for your thread to finish. Otherwise, your thread runs all by itself.
So one way to think of join() as a "hold" on the main thread -- it sort of de-threads your thread and executes sequentially in the main thread, before the main thread can continue. It assures that your thread is complete before the main thread moves forward. Note that this means it's ok if your thread is already finished before you call the join() -- the main thread is simply released immediately when join() is called.
In fact, it just now occurs to me that the main thread waits at d.join() until thread d finishes before it moves on to t.join().
In fact, to be very clear, consider this code:
import threadingimport timeclass Kiki(threading.Thread): def __init__(self, time): super(Kiki, self).__init__() self.time = time self.start() def run(self): print self.time, " seconds start!" for i in range(0,self.time): time.sleep(1) print "1 sec of ", self.time print self.time, " seconds finished!"t1 = Kiki(3)t2 = Kiki(2)t3 = Kiki(1)t1.join()print "t1.join() finished"t2.join()print "t2.join() finished"t3.join()print "t3.join() finished"
It produces this output (note how the print statements are threaded into each other.)
$ python test_thread.py32 seconds start! seconds start!1 seconds start!1 sec of 1 1 sec of 1 seconds finished! 21 sec of31 sec of 31 sec of 22 seconds finished!1 sec of 33 seconds finished!t1.join() finishedt2.join() finishedt3.join() finished$
The t1.join() is holding up the main thread. All three threads complete before the t1.join() finishes and the main thread moves on to execute the print then t2.join() then print then t3.join() then print.
Corrections welcome. I'm also new to threading.
(Note: in case you're interested, I'm writing code for a DrinkBot, and I need threading to run the ingredient pumps concurrently rather than sequentially -- less time to wait for each drink.)