Slide 1

Slide 1 text

CONCURRENCY IN PYTHON MOSKY 1

Slide 2

Slide 2 text

MULTITHREADING & 
 MULTIPROCESSING IN PYTHON MOSKY 2

Slide 3

Slide 3 text

MOSKY PYTHON CHARMER @ PINKOI 
 MOSKY.TW 3

Slide 4

Slide 4 text

OUTLINE 4

Slide 5

Slide 5 text

OUTLINE • Introduction 4

Slide 6

Slide 6 text

OUTLINE • Introduction • Producer-Consumer Pattern 4

Slide 7

Slide 7 text

OUTLINE • Introduction • Producer-Consumer Pattern • Python’s Flavor 4

Slide 8

Slide 8 text

OUTLINE • Introduction • Producer-Consumer Pattern • Python’s Flavor • Misc. Techiques 4

Slide 9

Slide 9 text

INTRODUCTION 5

Slide 10

Slide 10 text

MULTITHREADING 6

Slide 11

Slide 11 text

MULTITHREADING • GIL 6

Slide 12

Slide 12 text

MULTITHREADING • GIL • Only one thread runs at any given time. 6

Slide 13

Slide 13 text

MULTITHREADING • GIL • Only one thread runs at any given time. • It still can improves IO-bound problems. 6

Slide 14

Slide 14 text

MULTIPROCESSING 7

Slide 15

Slide 15 text

MULTIPROCESSING • It uses fork. 7

Slide 16

Slide 16 text

MULTIPROCESSING • It uses fork. • Processes can run at the same time. 7

Slide 17

Slide 17 text

MULTIPROCESSING • It uses fork. • Processes can run at the same time. • Use more memory. 7

Slide 18

Slide 18 text

MULTIPROCESSING • It uses fork. • Processes can run at the same time. • Use more memory. • Note the initial cost. 7

Slide 19

Slide 19 text

IS IT HARD? 8

Slide 20

Slide 20 text

IS IT HARD? • Avoid shared resources. 8

Slide 21

Slide 21 text

IS IT HARD? • Avoid shared resources. • e.g., vars or shared memory, files, connections, … 8

Slide 22

Slide 22 text

IS IT HARD? • Avoid shared resources. • e.g., vars or shared memory, files, connections, … • Understand Python’s flavor. 8

Slide 23

Slide 23 text

IS IT HARD? • Avoid shared resources. • e.g., vars or shared memory, files, connections, … • Understand Python’s flavor. • Then it will be easy. 8

Slide 24

Slide 24 text

SHARED RESOURCE 9

Slide 25

Slide 25 text

SHARED RESOURCE • Race condition:
 T1: RW
 T2: RW
 T1+T2: RRWW 9

Slide 26

Slide 26 text

SHARED RESOURCE • Race condition:
 T1: RW
 T2: RW
 T1+T2: RRWW • Use lock → Thread-safe:
 T1+T2: (RW) (RW) 9

Slide 27

Slide 27 text

SHARED RESOURCE • Race condition:
 T1: RW
 T2: RW
 T1+T2: RRWW • Use lock → Thread-safe:
 T1+T2: (RW) (RW) • But lock causes worse performance and deadlock. 9

Slide 28

Slide 28 text

SHARED RESOURCE • Race condition:
 T1: RW
 T2: RW
 T1+T2: RRWW • Use lock → Thread-safe:
 T1+T2: (RW) (RW) • But lock causes worse performance and deadlock. • Which is the hard part. 9

Slide 29

Slide 29 text

DIAGNOSE PROBLEM 10

Slide 30

Slide 30 text

DIAGNOSE PROBLEM • Where is the bottleneck? 10

Slide 31

Slide 31 text

DIAGNOSE PROBLEM • Where is the bottleneck? • Divide your problem. 10

Slide 32

Slide 32 text

PRODUCER-CONSUMER PATTERN 11

Slide 33

Slide 33 text

PRODUCER-CONSUMER PATTERN 12

Slide 34

Slide 34 text

PRODUCER-CONSUMER PATTERN • A queue 12

Slide 35

Slide 35 text

PRODUCER-CONSUMER PATTERN • A queue • Producers → A queue 12

Slide 36

Slide 36 text

PRODUCER-CONSUMER PATTERN • A queue • Producers → A queue • A queue → Consumers 12

Slide 37

Slide 37 text

PRODUCER-CONSUMER PATTERN • A queue • Producers → A queue • A queue → Consumers • Python has built-in Queue module for it. 12

Slide 38

Slide 38 text

EXAMPLES • https://docs.python.org/2/library/ queue.html#queue-objects • https://github.com/moskytw/mrbus/blob/master/ mrbus/base/pool.py 13

Slide 39

Slide 39 text

WHY .TASK_DONE? 14

Slide 40

Slide 40 text

WHY .TASK_DONE? • It’s for .join. 14

Slide 41

Slide 41 text

WHY .TASK_DONE? • It’s for .join. • When the counter goes zero, 
 it will notify the threads which are waiting. 14

Slide 42

Slide 42 text

WHY .TASK_DONE? • It’s for .join. • When the counter goes zero, 
 it will notify the threads which are waiting. • It’s implemented by threading.Condition. 14

Slide 43

Slide 43 text

15 THE THREADING MODULE

Slide 44

Slide 44 text

15 • Lock — primitive lock: .acquire / .release THE THREADING MODULE

Slide 45

Slide 45 text

15 • Lock — primitive lock: .acquire / .release • RLock — owner can reenter THE THREADING MODULE

Slide 46

Slide 46 text

15 • Lock — primitive lock: .acquire / .release • RLock — owner can reenter • Semaphore — lock when counter goes zero THE THREADING MODULE

Slide 47

Slide 47 text

16

Slide 48

Slide 48 text

• Condition — 
 .wait for .notify / .notify_all 16

Slide 49

Slide 49 text

• Condition — 
 .wait for .notify / .notify_all • Event — .wait for .set; simplifed Condition 16

Slide 50

Slide 50 text

• Condition — 
 .wait for .notify / .notify_all • Event — .wait for .set; simplifed Condition • with lock: … 16

Slide 51

Slide 51 text

THE MULTIPROCESSING MODULE 17

Slide 52

Slide 52 text

THE MULTIPROCESSING MODULE • .Process 17

Slide 53

Slide 53 text

THE MULTIPROCESSING MODULE • .Process • .JoinableQueue 17

Slide 54

Slide 54 text

THE MULTIPROCESSING MODULE • .Process • .JoinableQueue • .Pool 17

Slide 55

Slide 55 text

THE MULTIPROCESSING MODULE • .Process • .JoinableQueue • .Pool • … 17

Slide 56

Slide 56 text

PYTHON’S FLAVOR 18

Slide 57

Slide 57 text

19 DAEMONIC THREAD

Slide 58

Slide 58 text

19 • It’s not that “daemon”. DAEMONIC THREAD

Slide 59

Slide 59 text

19 • It’s not that “daemon”. • Just will be killed when Python shutting down. DAEMONIC THREAD

Slide 60

Slide 60 text

19 • It’s not that “daemon”. • Just will be killed when Python shutting down. • Immediately. DAEMONIC THREAD

Slide 61

Slide 61 text

19 • It’s not that “daemon”. • Just will be killed when Python shutting down. • Immediately. • Others keep running until return. DAEMONIC THREAD

Slide 62

Slide 62 text

SO, HOW TO STOP? 20

Slide 63

Slide 63 text

SO, HOW TO STOP? • Set demon and let Python clean it up. 20

Slide 64

Slide 64 text

SO, HOW TO STOP? • Set demon and let Python clean it up. • Let it return. 20

Slide 65

Slide 65 text

BUT, THE THREAD IS BLOCKING 21

Slide 66

Slide 66 text

BUT, THE THREAD IS BLOCKING • Set timeout. 21

Slide 67

Slide 67 text

HOW ABOUT CTRL+C? 22

Slide 68

Slide 68 text

HOW ABOUT CTRL+C? • Only main thread can receive that. 22

Slide 69

Slide 69 text

HOW ABOUT CTRL+C? • Only main thread can receive that. • BSD-style. 22

Slide 70

Slide 70 text

BROADCAST SIGNAL 
 TO SUB-THREAD 23

Slide 71

Slide 71 text

BROADCAST SIGNAL 
 TO SUB-THREAD • Set a global flag when get signal. 23

Slide 72

Slide 72 text

BROADCAST SIGNAL 
 TO SUB-THREAD • Set a global flag when get signal. • Let thread read it before each task. 23

Slide 73

Slide 73 text

BROADCAST SIGNAL 
 TO SUB-THREAD • Set a global flag when get signal. • Let thread read it before each task. • No, you can’t kill non-daemonic thread. 23

Slide 74

Slide 74 text

BROADCAST SIGNAL 
 TO SUB-THREAD • Set a global flag when get signal. • Let thread read it before each task. • No, you can’t kill non-daemonic thread. • Just can’t do so. 23

Slide 75

Slide 75 text

BROADCAST SIGNAL 
 TO SUB-THREAD • Set a global flag when get signal. • Let thread read it before each task. • No, you can’t kill non-daemonic thread. • Just can’t do so. • It’s Python. 23

Slide 76

Slide 76 text

BROADCAST SIGNAL 
 TO SUB-PROCESS 24

Slide 77

Slide 77 text

BROADCAST SIGNAL 
 TO SUB-PROCESS • Just broadcast the signal to sub-processes. 24

Slide 78

Slide 78 text

BROADCAST SIGNAL 
 TO SUB-PROCESS • Just broadcast the signal to sub-processes. • Start with register signal handler:
 signal(SIGINT, _handle_to_term_signal) 24

Slide 79

Slide 79 text

25

Slide 80

Slide 80 text

• Realize process context if need:
 pid = getpid()
 pgid = getpgid(0)
 proc_is_parent = (pid == pgid) 25

Slide 81

Slide 81 text

• Realize process context if need:
 pid = getpid()
 pgid = getpgid(0)
 proc_is_parent = (pid == pgid) • Off the handler:
 signal(signum, SIG_IGN) 25

Slide 82

Slide 82 text

• Realize process context if need:
 pid = getpid()
 pgid = getpgid(0)
 proc_is_parent = (pid == pgid) • Off the handler:
 signal(signum, SIG_IGN) • Broadcast:
 killpg(pgid, signum) 25

Slide 83

Slide 83 text

MISC. TECHIQUES 26

Slide 84

Slide 84 text

JUST THREAD IT OUT 27

Slide 85

Slide 85 text

JUST THREAD IT OUT • Or process it out. 27

Slide 86

Slide 86 text

JUST THREAD IT OUT • Or process it out. • Let main thread exit earlier. (Looks faster!) 27

Slide 87

Slide 87 text

JUST THREAD IT OUT • Or process it out. • Let main thread exit earlier. (Looks faster!) • Let main thread keep dispatching tasks. 27

Slide 88

Slide 88 text

JUST THREAD IT OUT • Or process it out. • Let main thread exit earlier. (Looks faster!) • Let main thread keep dispatching tasks. • “Async” 27

Slide 89

Slide 89 text

JUST THREAD IT OUT • Or process it out. • Let main thread exit earlier. (Looks faster!) • Let main thread keep dispatching tasks. • “Async” • And fix some stupid behavior.
 (I meant atexit with multiprocessing.Pool.) 27

Slide 90

Slide 90 text

COLLECT RESULT SMARTER 28

Slide 91

Slide 91 text

COLLECT RESULT SMARTER • Put into a safe queue. 28

Slide 92

Slide 92 text

COLLECT RESULT SMARTER • Put into a safe queue. • Use a thread per instance. 28

Slide 93

Slide 93 text

COLLECT RESULT SMARTER • Put into a safe queue. • Use a thread per instance. • Learn “let it go”. 28

Slide 94

Slide 94 text

EXAMPLES • https://github.com/moskytw/mrbus/blob/master/ mrbus/base/pool.py#L45 • https://github.com/moskytw/mrbus/blob/master/ mrbus/model/core.py#L30 29

Slide 95

Slide 95 text

MONITOR THEM 30

Slide 96

Slide 96 text

MONITOR THEM • No one is a master at first. 30

Slide 97

Slide 97 text

MONITOR THEM • No one is a master at first. • Don’t guess. 30

Slide 98

Slide 98 text

MONITOR THEM • No one is a master at first. • Don’t guess. • Just use a function to print log. 30

Slide 99

Slide 99 text

BENCHMARK THEM 31

Slide 100

Slide 100 text

BENCHMARK THEM • No one is a master at first. 31

Slide 101

Slide 101 text

BENCHMARK THEM • No one is a master at first. • Don’t guess. 31

Slide 102

Slide 102 text

BENCHMARK THEM • No one is a master at first. • Don’t guess. • Just prove it. 31

Slide 103

Slide 103 text

CONCLUSION 32

Slide 104

Slide 104 text

CONCLUSION • Avoid shared resource 
 — or just use producer-consumer pattern. 32

Slide 105

Slide 105 text

CONCLUSION • Avoid shared resource 
 — or just use producer-consumer pattern. • Signals only go main thread. 32

Slide 106

Slide 106 text

CONCLUSION • Avoid shared resource 
 — or just use producer-consumer pattern. • Signals only go main thread. • Just thread it out. 32

Slide 107

Slide 107 text

CONCLUSION • Avoid shared resource 
 — or just use producer-consumer pattern. • Signals only go main thread. • Just thread it out. • Collect your result smarter. 32

Slide 108

Slide 108 text

CONCLUSION • Avoid shared resource 
 — or just use producer-consumer pattern. • Signals only go main thread. • Just thread it out. • Collect your result smarter. • Monitor and benchmark your code. 32