[Python-ideas] Concurrency Modules

Nick Coghlan ncoghlan at gmail.com
Sat Jul 11 10:13:58 CEST 2015


On 11 July 2015 at 09:42, Steve Dower <Steve.Dower at microsoft.com> wrote:
> Processes and threads only really enter into asyncio as a "thing that can post messages back to my TODO list/event loop", while asyncio provides an efficient mechanism for interleaving (not parallelising) multiple tasks throughout an entire application (or a very significant self-contained piece of it). The parallelism only comes when all the main thread has to do for a particular task is wait, because another thread/process/service/device/etc. is doing the actual work.
>
> Hopefully that helps clear things up for some people. No example is perfect for everyone, ultimately, so the more we put out there the more likely

I really like this example, so I had a go at expressing it in the
foreground/background terms I use in
http://www.curiousefficiency.org/posts/2015/07/asyncio-tcp-echo-server.html

For folks that already know asyncio, the relevant semantic details of
that proposal for this example are:

    run_in_foreground -> convenience wrapper for run_until_complete
    run_in_background(coroutine) -> convenience wrapper for ensure_future
    run_in_background(callable) -> convenience wrapper for run_in_executor

I quite like the end result:

    # We'll need the concept of an oven
    class Oven:

        # There's a shared pool of ovens we can use
        @classmethod
        async def get_oven():
            ...

        # An oven can only have one set of current settings
        def configure(self, recipe):
            ...

        # An oven can only cook one thing at a time
       def bake(self, mixture):
            ...

    # We stay focused on this task
    def gather_ingredients(recipe):
        ...
        return ingredients

    # Helper to indicate readiness to switch tasks
    def survey_the_kitchen():
        return asyncio.sleep(0)

    # This task may be interleaved with other activities
   async def mix_ingredients(recipe, ingredients):
        mixture = CakeMixture(recipe)
        for ingredient in ingredients:
            mixture.add(ingredient)
            await survey_the_kitchen()
        return mixture

    # This task may be interleaved with other activities
    async def make_cake(recipe):
        # First, we gather and start mixing the ingredients
        ingredients = gather_ingredients(recipe)
        mixture = await mix_ingredients(recipe, ingredients)
        # We wait for a free oven, then configure it for our recipe
        oven = await Oven.get_oven()
        oven.configure(recipe)
        # Baking is synchronous for the *oven*, but *we* don't
        # want to sit around waiting for it the entire time
        bake_cake = functools.partial(oven.bake, mixture)
        return await run_in_background(bake_cake)

    # We have three cakes to make
    make_sponge = make_cake("sponge")
    make_madeira = make_cake("madeira")
    make_chocolate = make_cake("chocolate")

    # Which we'll try to do concurrently
    run_in_foreground(asyncio.wait([make_sponge, make_madeira, make_chocolate]))

    sponge_cake = make_sponge.result()
    madeira_cake = make_madeira.result()
    chocalate_cake = make_chocolate.result()

Now, to upgrade this to full event driven programming: imagine you're
modeling a professional bakery, accepting cake orders from customers.
Then you would need to define a server process that turns orders from
customers into cake making requests, and completed cake notifications
into delivery orders, and your main thread becomes devoted to running
that server, rather than specifying a pre-selected set of cakes to
make.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-ideas mailing list