Skip to content

Commit 75e957e

Browse files
Wrote a bit about threading
1 parent 1d25e8b commit 75e957e

File tree

1 file changed

+71
-3
lines changed

1 file changed

+71
-3
lines changed

docs/scenarios/speed.rst

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
354351
Threading
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

358423
Spawning 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

Comments
 (0)