@@ -226,9 +226,130 @@ Numba
226226-----
227227.. todo :: Write about Numba and the autojit compiler for NumPy
228228
229- Threading
230- :::::::::
229+ Concurrency
230+ :::::::::::
231+
232+
233+ Concurrent.futures
234+ ------------------
231235
236+ The `concurrent.futures `_ module is a module in the standard library that
237+ provides a "high-level interface for asynchronously executing callables". It
238+ abstracts away a lot of the more complicated details about using multiple
239+ threads or processes for concurrency, and allows the user to focus on
240+ accomplishing the task at hand.
241+
242+ The `concurrent.futures `_ module exposes two main classes, the
243+ `ThreadPoolExecutor ` and the `ProcessPoolExecutor `. The ThreadPoolExecutor
244+ will create a pool of worker threads that a user can submit jobs to. These jobs
245+ will then be executed in another thread when the next worker thread becomes
246+ available.
247+
248+ The ProcessPoolExecutor works in the same way, except instead of using multiple
249+ threads for its workers, it will use multiple processes. This makes it possible
250+ to side-step the GIL, however because of the way things are passed to worker
251+ processes, only picklable objects can be executed and returned.
252+
253+ Because of the way the GIL works, a good rule of thumb is to use a
254+ ThreadPoolExecutor when the task being executed involves a lot of blocking
255+ (i.e. making requests over the network) and to use a ProcessPoolExecutor
256+ executor when the task is computationally expensive.
257+
258+ There are two main ways of executing things in parallel using the two
259+ Executors. One way is with the `map(func, iterables) ` method. This works
260+ almost exactly like the builtin `map() ` function, except it will execute
261+ everything in parallel. ::
262+
263+ from concurrent.futures import ThreadPoolExecutor
264+ import requests
265+
266+ def get_webpage(url):
267+ """
268+ Some blocking function.
269+ """
270+ page = requests.get(url)
271+ return page
272+
273+ pool = ThreadPoolExecutor(max_workers=5)
274+
275+ my_urls = ['http://google.com/']*10 # Create a list of urls
276+
277+ for page in pool.map(get_webpage, my_urls):
278+ # Do something with the result
279+ print(page.text)
280+
281+ For even more control, the `submit(func, *args, **kwargs) ` method will schedule
282+ a callable to be executed ( as `func(*args, **kwargs) `) and returns a `Future `_
283+ object that represents the execution of the callable.
284+
285+ The Future object provides various methods that can be used to check on the
286+ progress of the scheduled callable. These include:
287+
288+ cancel()
289+ Attempt to cancel the call.
290+ cancelled()
291+ Return True if the call was successfully cancelled.
292+ running()
293+ Return True if the call is currently being executed and cannot be
294+ cancelled.
295+ done()
296+ Return True if the call was successfully cancelled or finished running.
297+ result()
298+ Return the value returned by the call. Note that this call will block until
299+ the scheduled callable returns by default.
300+ exception()
301+ Return the exception raised by the call. If no exception was raised then
302+ this returns `None `. Note that this will block just like `result() `.
303+ add_done_callback(fn)
304+ Attach a callback function that will be executed (as `fn(future) `) when the
305+ scheduled callable returns.
306+
307+ ::
308+
309+ from concurrent.futures import ProcessPoolExecutor, as_completed
310+
311+ def is_prime(n):
312+ if n % 2 == 0:
313+ return n, False
314+
315+ sqrt_n = int(n**0.5)
316+ for i in range(3, sqrt_n + 1, 2):
317+ if n % i == 0:
318+ return n, False
319+ return n, True
320+
321+ PRIMES = [
322+ 112272535095293,
323+ 112582705942171,
324+ 112272535095293,
325+ 115280095190773,
326+ 115797848077099,
327+ 1099726899285419]
328+
329+ futures = []
330+ with ProcessPoolExecutor(max_workers=4) as pool:
331+ # Schedule the ProcessPoolExecutor to check if a number is prime
332+ # and add the returned Future to our list of futures
333+ for p in PRIMES:
334+ fut = pool.submit(is_prime, p)
335+ futures.append(fut)
336+
337+ # As the jobs are completed, print out the results
338+ for number, result in as_completed(futures):
339+ if result:
340+ print("{} is prime".format(number))
341+ else:
342+ print("{} is not prime".format(number))
343+
344+ The `concurrent.futures `_ module contains two helper functions for working with
345+ Futures. The `as_completed(futures) ` function returns an iterator over the list
346+ of futures, yielding the futures as they complete.
347+
348+ The `wait(futures) ` function will simply block until all futures in the list of
349+ futures provided have completed.
350+
351+ For more information, on using the `concurrent.futures `_ module, consult the
352+ official documentation.
232353
233354Threading
234355---------
@@ -248,3 +369,5 @@ Multiprocessing
248369.. _`New GIL` : http://www.dabeaz.com/python/NewGIL.pdf
249370.. _`Special care` : http://docs.python.org/c-api/init.html#threads
250371.. _`David Beazley's` : http://www.dabeaz.com/GIL/gilvis/measure2.py
372+ .. _`concurrent.futures` : https://docs.python.org/3/library/concurrent.futures.html
373+ .. _`Future` : https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Future
0 commit comments