Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1d25e8b

Browse files
Added a bit about concurrent.futures
1 parent b620def commit 1d25e8b

File tree

1 file changed

+125
-2
lines changed

1 file changed

+125
-2
lines changed

docs/scenarios/speed.rst

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

233354
Threading
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

Comments
 (0)