HLab

Julien Hautefeuille

Programmation asynchrone avec Python 3.4 et le module asyncio

Introduction

Le module Python Asyncio est un module qui fournit une infrastructure pour écrire du code monothreadé concurrent en utilisant des coroutines, en multiplexant les Entrées/Sorties au-dessus de sockets ou d’autres ressources, et en démarrant des clients et des serveurs.

Les coroutines

Une coroutine est exécutée par l’expression yield from. Une coroutine est une fonction qui contient au moins une expression yield from.

Les tâches

Une tâche n’est pas exécutée par l’expression yield from. Une tâche est un objet qui gère une coroutine. Une tâche permet d’annuler une coroutine déjà exécutée. Une tâche permet d’exécuter une coroutine sous une coroutine existante.

Exemple de communication avec des websockets

Le projet Websockets permet d’utiliser simplement les websockets au-dessus du module asyncio. Ce sont ces websockets que nous allons utiliser pour réaliser le pattern consommateur/producteur.

Pour son installation :

sudo pip3.4 install websockets

Exemple :

import asyncio
import websockets

# Server
@asyncio.coroutine
def producer(websocket, uri):
    count = 0
    while websocket.open:
        yield from asyncio.sleep(1)
        count = count + 1
        yield from websocket.send(str(count))

# Client
@asyncio.coroutine
def consumer():
    websocket = yield from websockets.connect('ws://localhost:8765/')
    while websocket.open:
        count = yield from websocket.recv()
        print("%s" % count)

start_server = websockets.serve(producer, 'localhost', 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_until_complete(consumer())
asyncio.get_event_loop().run_forever()

Ce qui est intéressant dans ce cas, est que le consommateur peut être un client Javascript.

Exemple de communication avec une file d’attente

Dans cet exemple, nous allons utiliser une file d’attente illimitée pour échanger entre les coroutines. Si l’on choisit une file d’attente de taille fixe, celle-ci est bloquante si elle n’est pas vidée.

Exemple :

import asyncio

q = asyncio.Queue(maxsize=0)

@asyncio.coroutine
def producer():
    for elem in range(5):
        yield from q.put(elem)

@asyncio.coroutine
def consumer():
    while True:
        elems = yield from q.get()
        print(elems)

asyncio.get_event_loop().run_until_complete(producer())
asyncio.get_event_loop().run_until_complete(consumer())
asyncio.get_event_loop().run_forever()

L’intérêt ici, est l’utilisation d’une file d’attente sur laquelle on peut s’appuyer pour un contrôle de flux.

Liens

doc asyncio module