@@ -264,9 +264,6 @@ everything in parallel. ::
264264 import requests
265265
266266 def get_webpage(url):
267- """
268- Some blocking function.
269- """
270267 page = requests.get(url)
271268 return page
272269
@@ -354,6 +351,74 @@ official documentation.
354351Threading
355352---------
356353
354+ The standard library comes with a `threading `_ module that allows a user to
355+ work with multiple threads manually.
356+
357+ Running a function in another thread is as simple as passing a callable and
358+ it's arguments to `Thread `'s constructor and then calling `start() `::
359+
360+ from threading import Thread
361+ import requests
362+
363+ def get_webpage(url):
364+ page = requests.get(url)
365+ return page
366+
367+ some_thread = Thread(get_webpage, 'http://google.com/')
368+ some_thread.start()
369+
370+ To wait until the thread has terminated, call `join() `::
371+
372+ some_thread.join()
373+
374+ After calling `join() `, it is always a good idea to check whether the thread is
375+ still alive (because the join call timed out)::
376+
377+ if some_thread.is_alive():
378+ print("join() must have timed out.")
379+ else:
380+ print("Our thread has terminated.")
381+
382+ Because multiple threads have access to the same section of memory, sometimes
383+ there might be situations where two or more threads are trying to write to the
384+ same resource at the same time or where the output is dependent on the sequence
385+ or timing of certain events. This is called a `data race `_ or race condition.
386+ When this happens, the output will be garbled or you may encounter problems
387+ which are difficult to debug. A good example is this `stackoverflow post `_.
388+
389+ The way this can be avoided is by using a `Lock `_ that each thread needs to
390+ acquire before writing to a shared resource. Locks can be acquired and released
391+ through either the contextmanager protocol (`with ` statement), or by using
392+ `acquire() ` and `release() ` directly. Here is a (rather contrived) example::
393+
394+ from threading import Lock, Thread
395+
396+ file_lock = Lock()
397+
398+ def log(msg):
399+ with file_lock:
400+ open('website_changes.log', 'w') as f:
401+ f.write(changes)
402+
403+ def monitor_website(some_website):
404+ """
405+ Monitor a website and then if there are any changes, log them to disk.
406+ """
407+ while True:
408+ changes = check_for_changes(some_website)
409+ if changes:
410+ log(changes)
411+
412+ websites = ['http://google.com/', ... ]
413+ for website in websites:
414+ t = Thread(monitor_website, website)
415+ t.start()
416+
417+ Here, we have a bunch of threads checking for changes on a list of sites and
418+ whenever there are any changes, they attempt to write those changes to a file
419+ by calling `log(changes) `. When `log() ` is called, it will wait to acquire
420+ the lock with `with file_lock: `. This ensures that at any one time, only one
421+ thread is writing to the file.
357422
358423Spawning Processes
359424------------------
@@ -371,3 +436,6 @@ Multiprocessing
371436.. _`David Beazley's` : http://www.dabeaz.com/GIL/gilvis/measure2.py
372437.. _`concurrent.futures` : https://docs.python.org/3/library/concurrent.futures.html
373438.. _`Future` : https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Future
439+ .. _`threading` : https://docs.python.org/3/library/threading.html
440+ .. _`stackoverflow post` : http://stackoverflow.com/questions/26688424/python-threads-are-printing-at-the-same-time-messing-up-the-text-output
441+ .. _`data race` : https://en.wikipedia.org/wiki/Race_condition
0 commit comments