Implementing virtual streams without trickery

The gevent tutorial has an example that looks like this:

import gevent

def foo():
    print('Running in foo')
    gevent.sleep(0)
    print('Explicit context switch to foo again')

def bar():
    print('Explicit context to bar')
    gevent.sleep(0)
    print('Implicit context switch back to bar')

gevent.joinall([
    gevent.spawn(foo),
    gevent.spawn(bar),
])

      

and the output is

Running in foo
Explicit context to bar
Explicit context switch to foo again
Implicit context switch back to bar

      

I tested it and saw it myself work. A friend of mine claims that this is done entirely within a single thread. Except that I can't think of any implementation of gevent.sleep (0) that doesn't boil down to some form of "trickery" (ie, swapping the top two stack frames, etc.).

Can someone explain how this works? If it was Java (or at least some language that forbids such stack manipulation), is it possible? (again, without using multiple threads).

+3


source to share


1 answer


It really only works with one thread. gevent uses greenlets, which are coroutines, not threads. There is only 1 stack at any given time (unless you are using multithreading and then using potions on each thread).

In your example above, whenever you call sleep or joinall, the current coroutine (greenlet) actually gives the hub. Think of the hub as a central dispatcher, responsible for deciding which coroutine will run next.



To verify this, remove the call to gevent.sleep (0) and you will see that it behaves differently.

Note that, unlike threads, execution is deterministic, so if you run your program twice, it will execute in the same order.

0


source







All Articles