@@ -226,9 +226,130 @@ Numba
226
226
-----
227
227
.. todo :: Write about Numba and the autojit compiler for NumPy
228
228
229
- Threading
230
- :::::::::
229
+ Concurrency
230
+ :::::::::::
231
+
232
+
233
+ Concurrent.futures
234
+ ------------------
231
235
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.
232
353
233
354
Threading
234
355
---------
@@ -248,3 +369,5 @@ Multiprocessing
248
369
.. _`New GIL` : http://www.dabeaz.com/python/NewGIL.pdf
249
370
.. _`Special care` : http://docs.python.org/c-api/init.html#threads
250
371
.. _`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