• entries
8
11
• views
18386

Coroutines: Wierd Little Wonders

2447 views

So, what does multiple entry points mean?

Take a subroutines for example, when you call it, it executes it contents, then returns. If you call it exactly the same again, it will behave the same way again using.

so if you called:int subroutine(int a){ return a + 5;}
you would expect it always to increment 'a' by 5. Coroutines however are a different beast altogether especially since they have multiple entry points. The easiest way to demonstrate the difference between a coroutine is by looking at the below:int coroutine(int a){ return a + 5; return a + 4; return a + 3; return a + 2; return a + 1;}
in this case if you called the coroutine above, you would expect the following output:print coroutine(5); //10print coroutine(5); //9print coroutine(5); //8print coroutine(5); //7print coroutine(5); //6print coroutine(5); //???could be 6 or error or it starts over or other depending on implementation
That just looks like a state machine!
Well, its not. What is not shown is that coroutines maintain their state in-between calls, they don't switch to another. So "local" variables called earlier in the coroutine are still available in the coroutine's context. like so:int coroutine(int a){ int b = a * 2; return a + b; b = a * 1.5; b = return a + b; return b - a;}
Here, you may noticed i did something weird. That 2nd to last line i wrote "b = return a + b". Depending on implementation, coroutines can allow you to do all sorts of cool things like that. Python 3.3 in particular has a very cool "yield from" ability that allows something like this. But the main point is that 'b' remains local to the coroutine and doesn't just disappear after the coroutine returns.

Pfff, so its just a weird iterator!
Yes, yes it is, in a weird way. Think of them more as an enhanced iterator in that coroutines can yield not only their operations, but yield to other coroutines, and those can yield to others, etc etc. Take a look at this great Python pseudocode example of two generators (a python iterator):def gen_a(a): while True: b = yield from gen_b(a) yield b + 3def gen_b(a): b = 0 while True: yield b if b > 100: return b b += a def main(): for i in gen_a(5): print(i)
In this example, we iterate over the output of gen_a forever. However, while its iterating, gen_a yields from gen_b until b is incremented over 100, ad which point it returns b and breaks out of its loop, allowing gen_a to then yield b + 3 at which point it begins again. This example shows two things, how to "terminate" a generator early, as well as how to yield results from another generator. However, thats not really the important thing, the important thing here is that coroutines are very similar to generators and iterators, in-fact, most languages build coroutines off these constructs. Addditionally, what is important is the concept of yielding behavior to another coroutine/iterator/generator, which demonstrates the 'co' part of 'coroutine'.

now we'll take this concept and do something different:def handle_request(): request = yield from get_request() #do some processing or whatever f = file(request.uri) data = yield from f.read() yield from response.send(data)def server(): while True: yield from handle_request()def main() serv_gen = server() while True: serv_gen.next()
So, in the above example, i create a forever loop calling server's "next" method, which is typically how you call a coroutine without using a "yield" or "yield from" keyword and causes a coroutine in python to re-enter and run to the next yield statement. In that case, it attempts to handle a request, which in turn attempts to get a request, yielding a result into request. Interesting aspect here is that this all happens over multiple calls to serv_gen.next()... This simple server can server some content (not really, its just pseudocode), but it takes multiple calls to do it. Think on that a second.

Ok, i thought about it... doesnt that make them take longer?

Perhaps it would take longer if i was just running 1 coroutine vs 1 subroutine as i am jumping in and out of the iteratiors/coroutines. But what if i wanted to run 2 or 3 or 8 or 100? all at the same time... do you see it now? do you see how coroutines start having a significant advantage over subroutines?

Ok, so now what?

well, this is just to get you thinking about coroutines. In further entries i'll be showing how to build a coroutine framework in c#, as well as demonstrate their use in ways such as building a simple HTTP server capable of serving 30,000+ requests/sec (static content). Eventually, we'll get around to showing how their constructs may be used to create the foundation of a concurrent game engine. But until then, grab python 3.3, the tulip library and play around or if you're curious, check out my in-work library over at github: AsyncCS

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account