by Matias Guijarro
Simultaneous execution
Potentially interacting tasks
Examples
Concurrency implies multitasking
If only 1 CPU is available, the only way to run multiple tasks is by rapidly switching between them
For I/O, tasks must wait (sleep)
Behind the scenes, the underlying system will carry out I/O operation and wake the task when it's finished
Traditional multithreading
"Actor model" (from Erlang)
Async or Evented I/O
What most programmers think of when they hear about "concurrent programming"
Independent task running inside a parent program
Simultaneous access to objects
Often a source of unspeakable peril
Threads share all data in your program
Thread scheduling is non-deterministic
Operations often take several steps and might be interrupted mid-stream (non-atomicity)
Thus, access to any kind of shared data is also non-deterministic !
Race conditions
Threads needs to be synchronized
Global Interpreter Lock
Whenever a thread run, it holds the GIL
Multiprocessing module is part of standard Python since 2.6
Messaging implies serialization
A task is "CPU bound" if it spends most of its time with little I/O
Examples
A task is "I/O bound" if it spends most of its time waiting for I/O
Examples
Most programs are I/O bound
"non-blocking I/O"
permits other processing to continue before I/O operation has completed
more scalable than threads or processes (much less memory consumption)
usually gives great response time, latency and CPU usage on I/O bound programs
one single main thread (SPED: Single Process Event Driven)
No silver bullet, though
Recent progress on Linux kernel since 2.6 (better threading) and 64-bits architecture (more memory) make it less interesting than before in term of pure performance
Files are taken from the W3C web site: http://www.w3.org
1 files = ("/TR/html401/html40.txt",
2 "/TR/2002/REC-xhtml1-20020801/xhtml1.pdf",
3 "/TR/REC-html32.html",
4 "/TR/2000/REC-DOM-Level-2-Core-20001113/DOM2-Core.txt")
1 class download_task(threading.Thread):
2 def __init__(self, host, filepath, proxy="proxy.esrf.fr:3128"):
3 threading.Thread.__init__(self)
4
5 self.host = host
6 self.filepath = filepath
7 self.proxy = proxy
8 self.file_contents = ""
9
10 def run(self):
11 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
12 proxy_host, proxy_port = self.proxy.split(":")
13 s.connect((proxy_host, int(proxy_port)))
14
15 s.send("GET http://"+self.host+self.filepath+" HTTP/1.0\r\n\r\n")
16
17 buf = []
18 while True:
19 data = s.recv(1024)
20 if not data:
21 break
22 buf.append(data)
23
24 s.close()
25 self.file_contents = "".join(buf)
26
27 tasks = []
28 for filepath in files:
29 tasks.append(download_task("www.w3.org", filepath, "proxy.esrf.fr:3128"))
30 [task.start() for task in tasks]
31 [task.join() for task in tasks]
Use of the 'asyncore' standard Python module. Callbacks are fired when socket is ready to do a non-blocking read or write operation.
1 class download_task(asyncore.dispatcher):
2 def __init__(self, host, filepath, proxy="proxy.esrf.fr:3128"):
3 asyncore.dispatcher.__init__(self)
4
5 self.host = host
6 self.filepath = filepath
7 self.proxy = proxy
8 self.buffer = []
9 self.file_contents = ""
10 self.request_sent = False
11
12 def start(self):
13 self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
14 proxy_host, proxy_port = self.proxy.split(":")
15 self.connect((proxy_host, int(proxy_port)))
16
17 def handle_close(self):
18 self.close()
19 self.file_contents = "".join(self.buffer)
20
21 def handle_read(self):
22 data = self.recv(1024)
23 self.buffer.append(data)
24
25 def writable(self):
26 return not self.request_sent
27
28 def handle_write(self):
29 self.send("GET http://"+self.host+self.filepath+" HTTP/1.0\r\n\r\n")
30 self.request_sent = True
31
32 tasks = []
33 for filepath in files:
34 tasks.append(download_task("www.w3.org", filepath, "proxy.esrf.fr:3128"))
35 [task.start() for task in tasks]
36
37 asyncore.loop()
Fully asynchronous without callbacks
1 def download_task(host, filepath, proxy="proxy.esrf.fr:3128"):
2 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3 proxy_host, proxy_port = proxy.split(":")
4 self.connect((proxy_host, int(proxy_port)))
5
6 s.send("GET http://"+host+filepath+" HTTP/1.0\r\n\r\n")
7
8 buf = []
9 while True:
10 data = s.recv(1024)
11 if not data:
12 break
13 buf.append(data)
14
15 s.close()
16 return "".join(buf)
17
18 tasks = []
19 for filepath in files:
20 tasks.append(gevent.spawn(download_task, "www.w3.org", filepath, "proxy.esrf.fr:3128"))
21 gevent.joinall(tasks)
greenlet provides coroutines to Python via a C extension module
spin-off of Stackless, a version of Python supporting micro-threads
the idea of coroutine is from a 1963 paper from Melvin Conway
gevent trick #1: yield automatically when doing blocking I/O (or when 'sleeping')
Execution flow example
Greenlets execution is deterministic. No preemption.
Cooperative scheduling
Python allows for most objects to be modified at runtime including modules, classes, and even functions
gevent patches blocking system calls in the standard library including those in socket, ssl, threading and select modules to instead behave cooperatively
Beware
Timeouts
1 with gevent.Timeout(5):
2 response = urllib2.urlopen(url)
3 for line in response:
4 print line
5 # raises Timeout exception if not done after 5 seconds
DAQ and BL control is 99% I/O bound tasks
gevent scheduler + Python = sequencer
Already benefits to mxCuBE since May, 2012
Allows to concentrate on experiments problems, not concurrency issues
originally written for mxCuBE
a small layer on top of gevent, for our business
brings "task" decorator and cleanup/error_cleanup context managers
Inspired from MX data collection code; in mxCuBE, safety_shutter, detm, phi, etc. are already gevent-friendly objects. Most methods use the "task" decorator, to be used as such by upper level sequences.
1 @task
2 def my_sequence(DET_POS, START, END, N_IMAGES, EXP_T):
3 with cleanup(safety_shutter.close):
4 # detm move is slow; don't wait for end of move here
5 detector_move_task = detm.move(DET_POS, wait=False)
6
7 safety_shutter.open()
8
9 i0_diode.adjust_gain()
10
11 phi.move(START)
12
13 detector_move_task.join()
14
15 pilatus.prepare(N_IMAGES)
16 with error_cleanup(pilatus.reset):
17 oscillation(START, END, EXP_T)
18
19 my_sequence(timeout = 5)
Scientists and Pythonistas love IPython
Let's mix gevent with IPython
1 import gevent
2 # monkey-patch everything except threads
3 from gevent import monkey; monkey.patch_all(thread=False)
4 from IPython.core.interactiveshell import InteractiveShell
5 from IPython.lib.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready
6 from IPython.lib.inputhook import InputHookManager
7
8 def create_inputhook_gevent(mgr):
9 # Create an input hook for running the gevent event loop.
10
11 ip = InteractiveShell.instance()
12 if hasattr(ip, '_inputhook_gevent'):
13 return ip._inputhook_gevent
14
15 got_kbdint = [False]
16
17 def inputhook_gevent():
18 # PyOS_InputHook python hook for Gevent
19 try:
20 ignore_CTRL_C()
21 gevent.sleep(0.01) ### THIS RUNS THE LOOP FOR 10 ms
22 while not stdin_ready():
23 gevent.sleep(0.05) ### THIS RUNS THE LOOP FOR 50 ms
24 except:
25 ignore_CTRL_C()
26 from traceback import print_exc
27 print_exc()
28 finally:
29 allow_CTRL_C()
30 return 0
31
32 def preprompthook_gevent(ishell):
33 if got_kbdint[0]:
34 mgr.set_inputhook(inputhook_gevent)
35 got_kbdint[0] = False
36
37 ip._inputhook_gevent = inputhook_gevent
38 ip.set_hook('pre_prompt_hook', preprompthook_gevent)
39
40 return inputhook_gevent
41
42 def enable_gevent_hook():
43 mgr = InputHookManager()
44 mgr.set_inputhook(create_inputhook_gevent(mgr))
Starting IPython using the gevent hook defined on the previous slide
1 #!/usr/bin/python
2 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
3
4 shell = TerminalInteractiveShell()
5
6 enable_gevent_hook()
7
8 shell.mainloop()
Good news: IPython 0.14 will support gevent natively !
Would be great to have a gevent-friendly Python Tango client
Ask forgiveness, not permission
Building on top of gevent is promising
Nothing revolutionary (gevent is just a tool...)
Simplicity is the key
| Table of Contents | t |
|---|---|
| Exposé | ESC |
| Full screen slides | e |
| Presenter View | p |
| Source Files | s |
| Slide Numbers | n |
| Toggle screen blanking | b |
| Show/hide slide context | c |
| Notes | 2 |
| Help | h |