title: What exactly does Thread.join() do in python? Is this incorrect usage of Thread.join()?
body: <p>I've very recently started learning how to write multi-threaded programs in python and for starters, began experimenting <strong>without</strong> using queues.</p>
  <p>In the code below, loadFunction is just a sample target function for the threads. It's <strong>supposed</strong> to wait until all of the threads provided in the wait (list) argument have finished executing (and I'm trying to achieve this using join()). Then <strong>afterwards</strong> start printing out the specified number range.
  This is the desired behavior.</p>
  <p>In the main program, I created two threads which do not wait for any other threads to begin counting. Then I created a third thread, which is supposed to wait for the first two threads to finish executing before it begins counting.</p>
  <p>However that is not happening. On testing, I find that the three threads start executing simultaneously and the third thread does not wait for the first two to finish executing like I intended.</p>
  <p><em>So my question here is, what knowledge am I missing about the Thread.join() function, and what changes can I make to my code to achieve the desired result?</em></p>
  <p><strong>Code:</strong></p>
  <pre><code>&quot;&quot;&quot; Note: Python 3.3 onwards required since daemon was added as an initializable
      property from python 3.3 onwards.&quot;&quot;&quot;
  
  import threading
  
  def loadFunction(name, start, end, wait=[]):
      &quot;&quot;&quot; wait should be a list of threads to wait for &quot;&quot;&quot;
      map(lambda th: th.join(), wait)
      for number in range(start, end):
          print(&quot;%s : %d&quot; % (name, number))
  
  if __name__ == &quot;__main__&quot;:
      t1 = threading.Thread(target=loadFunction, args=(&quot;Thread1&quot;, 1, 101), name=&quot;Thread1&quot; ,daemon=True)
      t2 = threading.Thread(target=loadFunction, args=(&quot;Thread2&quot;, 101, 201), name=&quot;Thread2&quot;, daemon=True)
      t3 = threading.Thread(target=loadFunction, args=(&quot;Thread3&quot;, 1000, 1101, [t1, t2]), name=&quot;Thread3&quot;, daemon=True)
  
      t1.start()
      t2.start()
      t3.start()
  
      # wait for all of the daemon processes to finish before we close the program
      t1.join()
      t2.join()
      t3.join()
  
      print(&quot;done!&quot;)
  </code></pre>
  <p><strong>part of the result (in one run):</strong></p>
  <pre><code>Thread1 : 1
  Thread1 : 2
  Thread1 : 3
  Thread1 : 4
  Thread1 : 5
  Thread1 : 6
  Thread2 : 101
  Thread2 : 102
  Thread2 : 103
  Thread2 : 104
  Thread2 : 105
  Thread2 : 106
  Thread2 : 107
  Thread2 : 108
  Thread2 : 109
  Thread1 : 7
  Thread1 : 8
  Thread1 : 9
  Thread1 : 10
  Thread1 : 11
  Thread3 : 1000
  Thread1 : 12
  Thread1 : 13
  Thread3 : 1001
  Thread1 : 14
  Thread3 : 1002
  Thread1 : 15
  Thread3 : 1003
  Thread1 : 16
  Thread2 : 110
  </code></pre>
  <p>Here are a two things that were going through my mind when I was writing this code (as quoted from <a href="https://docs.python.org/3/library/threading.html" rel="nofollow noreferrer">the official documentation</a>):</p>
  <blockquote>
  <p>join(timeout=None)</p>
  <p>Wait until the thread terminates. This blocks the calling
  thread until the thread whose join() method is called terminates ...</p>
  </blockquote>
  <p>In reference to my example code, would the &quot;calling thread&quot; be whichever thread called the loadFunction function? I have a <em><strong>slight</strong></em> suspicion that this isn't the case and that the process itself called the function and not the thread hence the thread won't be the one waiting but the process waits instead. If this is the case, how would I fix it? I have a feeling that queues would be involved... <strong>if</strong> this is the reason in the first place.</p>
  <p>and:</p>
  <blockquote>
  <p>A thread can be join()ed many times.</p>
  </blockquote>
  <p>is what lead me to use join for the same thread twice.</p>
  <p>P.S. I'm learning about threading for the first time in python, but this is after learning forking processes in C, so maybe I <em><strong>might</strong></em> have gotten something confused here due to this. If the two aren't related, then I apologize, I'm under the impression that the two are similar (though clearly not the same since one splits the process but the other creates threads within the process itself).</p>
  <p>Thank you.</p>