I. Background
We all know that the mechanism of gevent is a single-threaded + concurrent mechanism, when encountered may block the operation, it will be switched to the runnable concurrent to continue to run, in order to achieve the goal of submitting the system operating efficiency, but how to achieve it? Let us look directly from the code.
II. Switching mechanisms
Let's start with the send, recv methods of the socket:
def recv(self, *args): while 1: try: return self._sock.recv(*args) except error as ex: if [0] != EWOULDBLOCK or == 0.0: raise # QQQ without clearing exc_info test__refcount.test_clean_exit fails sys.exc_clear() self._wait(self._read_event)
A dead loop will be opened here, in which the self._sock.recv() method is called and exceptions are caught, and when the error is EWOULDBLOCK, the self._wait(self._read_event) method is called, which is actually: _wait = _wait_on_socket, _wait_ on_socket method is defined in the file: _hub_primitives.py as follows:
# Suitable to be bound as an instance method def wait_on_socket(socket, watcher, timeout_exc=None): if socket is None or watcher is None: # test__hub TestCloseSocketWhilePolling, on Python 2; Python 3 # catches the EBADF differently. raise ConcurrentObjectUseError("The socket has already been closed by another greenlet") _primitive_wait(watcher, , timeout_exc if timeout_exc is not None else _NONE, )
The method actually calls the function: _primitive_wait(), which is still defined in the file: _hub_primitives.py as follows:
def _primitive_wait(watcher, timeout, timeout_exc, hub): if is not None: raise ConcurrentObjectUseError('This socket is already used by another greenlet: %r' % (, )) if hub is None: hub = get_hub() if timeout is None: (watcher) return timeout = Timeout._start_new_or_dummy( timeout, (timeout_exc if timeout_exc is not _NONE or timeout is None else _timeout_error('timed out'))) with timeout: (watcher)
Here the () function is actually called, which is defined in the file _hub.py as follows:
class WaitOperationsGreenlet(SwitchOutGreenletWithLoop): # pylint:disable=undefined-variable def wait(self, watcher): """ Wait until the *watcher* (which must not be started) is ready. The current greenlet will be unscheduled during this time. """ waiter = Waiter(self) # pylint:disable=undefined-variable (, waiter) try: result = () if result is not waiter: raise InvalidSwitchError( 'Invalid switch into %s: got %r (expected %r; waiting on %r with %r)' % ( getcurrent(), # pylint:disable=undefined-variable result, waiter, self, watcher ) ) finally: ()
()
The class WaitOperationsGreenlet is the base class of Hub, the logic in its method wait is: generate a Waiter object and call the (, waiter) method, the watcher is the self._read_event used in the very beginning of the recv method, the watcher is the underlying event of the gevent concept in the framework libev; there is also a waiter object, which is similar to the concept of future in python, the object has a switch() method as well as a get() method, when the result is not obtained is not ready, call the () method back to cause the concatenation to be hung; get() function is defined as follows:
def get(self): """If a value/an exception is stored, return/raise it. Otherwise until switch() or throw() is called.""" if self._exception is not _NONE: if self._exception is None: return getcurrent().throw(*self._exception) # pylint:disable=undefined-variable else: if is not None: raise ConcurrentObjectUseError('This Waiter is already used by %r' % (, )) = getcurrent() # pylint:disable=undefined-variable try: return () finally: = None
In get () in the most critical () function, the function will be transferred to the execution of the right to the hub, and continue to run, so far has been analyzed when the worker coprogram to get data from the network encountered blocking, how to avoid blocking and switch to the implementation of the hub, as for when to switch to the worker coprogram, we continue to analyze the follow-up.
summarize
It is important to remember an important concept in gevent, that a concatenation switch is not an invocation but a transfer of execution, from a potentially blocking concatenation to the hub, and a switch by the hub at the right time to another concatenation that can continue to run to continue its execution; gevent achieves the goal of increasing the throughput of io-intensive applications in this form.
This is the whole content of this article, I hope it will help you to learn more.