Skip to content

Commit ff436f9

Browse files
committed
examples for ch.20: Concurrency Models
1 parent bf4a2be commit ff436f9

File tree

7 files changed

+308
-0
lines changed

7 files changed

+308
-0
lines changed

20-concurrency/primes/primes.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import math
2+
import itertools
3+
4+
5+
PRIME_FIXTURE = [
6+
(2, True),
7+
(142702110479723, True),
8+
(299593572317531, True),
9+
(3333333333333301, True),
10+
(3333333333333333, False),
11+
(3333335652092209, False),
12+
(4444444444444423, True),
13+
(4444444444444444, False),
14+
(4444444488888889, False),
15+
(5555553133149889, False),
16+
(5555555555555503, True),
17+
(5555555555555555, False),
18+
(6666666666666666, False),
19+
(6666666666666719, True),
20+
(6666667141414921, False),
21+
(7777777536340681, False),
22+
(7777777777777753, True),
23+
(7777777777777777, False),
24+
(9999999999999917, True),
25+
(9999999999999999, False),
26+
]
27+
28+
NUMBERS = [n for n, _ in PRIME_FIXTURE]
29+
30+
# tag::IS_PRIME[]
31+
def is_prime(n) -> bool:
32+
if n < 2:
33+
return False
34+
if n == 2:
35+
return True
36+
if n % 2 == 0:
37+
return False
38+
39+
root = int(math.floor(math.sqrt(n)))
40+
for i in range(3, root + 1, 2):
41+
if n % i == 0:
42+
return False
43+
return True
44+
# end::IS_PRIME[]
45+
46+
if __name__ == '__main__':
47+
48+
for n, prime in PRIME_FIXTURE:
49+
prime_res = is_prime(n)
50+
assert prime_res == prime
51+
print(n, prime)

20-concurrency/primes/procs.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# tag::PRIMES_PROC_TOP[]
2+
from time import perf_counter
3+
from typing import Tuple, List, NamedTuple
4+
from multiprocessing import Process, SimpleQueue # <1>
5+
from multiprocessing import queues # <2>
6+
7+
from primes import is_prime, NUMBERS
8+
9+
class Result(NamedTuple): # <3>
10+
flag: bool
11+
elapsed: float
12+
13+
JobQueue = queues.SimpleQueue[Tuple[int, Result]] # <4>
14+
15+
def check(n: int) -> Result: # <5>
16+
t0 = perf_counter()
17+
res = is_prime(n)
18+
return Result(res, perf_counter() - t0)
19+
20+
def job(n: int, results: JobQueue) -> None: # <6>
21+
results.put((n, check(n))) # <7>
22+
# end::PRIMES_PROC_TOP[]
23+
24+
# tag::PRIMES_PROC_MAIN[]
25+
def main() -> None:
26+
t0 = perf_counter()
27+
results: JobQueue = SimpleQueue() # <1>
28+
workers: List[Process] = [] # <2>
29+
30+
for n in NUMBERS:
31+
worker = Process(target=job, args=(n, results)) # <3>
32+
worker.start() # <4>
33+
workers.append(worker) # <5>
34+
35+
for _ in workers: # <6>
36+
n, (prime, elapsed) = results.get() # <7>
37+
label = 'P' if prime else ' '
38+
print(f'{n:16} {label} {elapsed:9.6f}s')
39+
40+
41+
time = perf_counter() - t0
42+
print('Total time:', f'{time:0.2f}s')
43+
44+
if __name__ == '__main__':
45+
main()
46+
# end::PRIMES_PROC_MAIN[]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from time import perf_counter
2+
from typing import NamedTuple
3+
4+
from primes import is_prime, NUMBERS
5+
6+
class Result(NamedTuple): # <1>
7+
flag: bool
8+
elapsed: float
9+
10+
def check(n: int) -> Result: # <2>
11+
t0 = perf_counter()
12+
flag = is_prime(n)
13+
return Result(flag, perf_counter() - t0)
14+
15+
def main() -> None:
16+
t0 = perf_counter()
17+
for n in NUMBERS: # <3>
18+
prime, elapsed = check(n)
19+
label = 'P' if prime else ' '
20+
print(f'{n:16} {label} {elapsed:9.6f}s')
21+
22+
elapsed = perf_counter() - t0 # <4>
23+
print('Total time:', f'{elapsed:0.2f}s')
24+
25+
if __name__ == '__main__':
26+
main()
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# spinner_async_experiment.py
2+
3+
# credits: Example by Luciano Ramalho inspired by
4+
# Michele Simionato's multiprocessing example in the python-list:
5+
# https://mail.python.org/pipermail/python-list/2009-February/675659.html
6+
7+
import asyncio
8+
import itertools
9+
import math
10+
11+
# tag::SPINNER_ASYNC_NAP[]
12+
async def is_prime(n):
13+
if n < 2:
14+
return False
15+
if n == 2:
16+
return True
17+
if n % 2 == 0:
18+
return False
19+
20+
sleep = asyncio.sleep # <1>
21+
root = int(math.floor(math.sqrt(n)))
22+
for i in range(3, root + 1, 2):
23+
if n % i == 0:
24+
return False
25+
if i % 100_000 == 1: # <2>
26+
await sleep(0)
27+
return True
28+
# end::SPINNER_ASYNC_NAP[]
29+
30+
31+
async def spin(msg: str) -> None:
32+
for char in itertools.cycle(r'\|/-'):
33+
status = f'\r{char} {msg}'
34+
print(status, flush=True, end='')
35+
try:
36+
await asyncio.sleep(.1)
37+
except asyncio.CancelledError:
38+
break
39+
blanks = ' ' * len(status)
40+
print(f'\r{blanks}\r', end='')
41+
42+
async def slow() -> int:
43+
await is_prime(5_000_111_000_222_021) # <4>
44+
return 42
45+
46+
async def supervisor() -> int:
47+
spinner = asyncio.create_task(spin('thinking!')) # <1>
48+
print('spinner object:', spinner) # <2>
49+
result = await slow() # <3>
50+
spinner.cancel() # <5>
51+
return result
52+
53+
def main() -> None:
54+
result = asyncio.run(supervisor())
55+
print('Answer:', result)
56+
57+
if __name__ == '__main__':
58+
main()
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# spinner_async_experiment.py
2+
3+
# credits: Example by Luciano Ramalho inspired by
4+
# Michele Simionato's multiprocessing example in the python-list:
5+
# https://mail.python.org/pipermail/python-list/2009-February/675659.html
6+
7+
import asyncio
8+
import itertools
9+
10+
import primes
11+
12+
async def spin(msg: str) -> None:
13+
for char in itertools.cycle(r'\|/-'):
14+
status = f'\r{char} {msg}'
15+
print(status, flush=True, end='')
16+
try:
17+
await asyncio.sleep(.1)
18+
except asyncio.CancelledError:
19+
break
20+
print('THIS WILL NEVER BE OUTPUT')
21+
22+
# tag::SPINNER_ASYNC_EXPERIMENT[]
23+
async def slow() -> int:
24+
primes.is_prime(5_000_111_000_222_021) # <4>
25+
return 42
26+
27+
async def supervisor() -> int:
28+
spinner = asyncio.create_task(spin('thinking!')) # <1>
29+
print('spinner object:', spinner) # <2>
30+
result = await slow() # <3>
31+
spinner.cancel() # <5>
32+
return result
33+
# end::SPINNER_ASYNC_EXPERIMENT[]
34+
35+
def main() -> None:
36+
result = asyncio.run(supervisor())
37+
print('Answer:', result)
38+
39+
if __name__ == '__main__':
40+
main()
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# spinner_thread.py
2+
3+
# credits: Adapted from Michele Simionato's
4+
# multiprocessing example in the python-list:
5+
# https://mail.python.org/pipermail/python-list/2009-February/675659.html
6+
7+
# tag::SPINNER_THREAD_TOP[]
8+
from threading import Thread, Event
9+
import itertools
10+
import time
11+
12+
from primes import is_prime
13+
14+
def spin(msg: str, done: Event) -> None: # <1>
15+
for char in itertools.cycle(r'\|/-'): # <2>
16+
status = f'\r{char} {msg}' # <3>
17+
print(status, end='', flush=True)
18+
if done.wait(.1): # <4>
19+
break # <5>
20+
blanks = ' ' * len(status)
21+
print(f'\r{blanks}\r', end='') # <6>
22+
23+
def slow() -> int:
24+
is_prime(5_000_111_000_222_021) # <7>
25+
return 42
26+
# end::SPINNER_THREAD_TOP[]
27+
28+
# tag::SPINNER_THREAD_REST[]
29+
def supervisor() -> int: # <1>
30+
done = Event() # <2>
31+
spinner = Thread(target=spin,
32+
args=('thinking!', done)) # <3>
33+
print('spinner object:', spinner) # <4>
34+
spinner.start() # <5>
35+
result = slow() # <6>
36+
done.set() # <7>
37+
spinner.join() # <8>
38+
return result
39+
40+
def main() -> None:
41+
result = supervisor() # <9>
42+
print('Answer:', result)
43+
44+
if __name__ == '__main__':
45+
main()
46+
# end::SPINNER_THREAD_REST[]

20-concurrency/primes/threads.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from time import perf_counter
2+
from typing import Tuple, List, NamedTuple
3+
from threading import Thread
4+
from queue import SimpleQueue
5+
6+
from primes import is_prime, NUMBERS
7+
8+
class Result(NamedTuple): # <3>
9+
flag: bool
10+
elapsed: float
11+
12+
JobQueue = SimpleQueue[Tuple[int, Result]] # <4>
13+
14+
def check(n: int) -> Result: # <5>
15+
t0 = perf_counter()
16+
res = is_prime(n)
17+
return Result(res, perf_counter() - t0)
18+
19+
def job(n: int, results: JobQueue) -> None: # <6>
20+
results.put((n, check(n))) # <7>
21+
22+
def main() -> None:
23+
t0 = perf_counter()
24+
results: JobQueue = SimpleQueue() # <1>
25+
workers: List[Thread] = [] # <2>
26+
27+
for n in NUMBERS:
28+
worker = Thread(target=job, args=(n, results)) # <3>
29+
worker.start() # <4>
30+
workers.append(worker) # <5>
31+
32+
for _ in workers: # <6>
33+
n, (prime, elapsed) = results.get() # <7>
34+
label = 'P' if prime else ' '
35+
print(f'{n:16} {label} {elapsed:9.6f}s')
36+
37+
time = perf_counter() - t0
38+
print('Total time:', f'{time:0.2f}s')
39+
40+
if __name__ == '__main__':
41+
main()

0 commit comments

Comments
 (0)