'''
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
---------------------------------------------------------
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()
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'''