• [^] # Re: Rx

    Posté par (site web personnel) . En réponse au journal La programmation concurrente en mode Goto. Évalué à 1.

    Tu te contredis entre ta première et ta seconde phrase...

    C'est bien possible, j'ai galéré pour trouver des définitions de asynchrone/concurrente/parallèle compatibles entre elles !
    Je viens de refaire un tour sur wikipedia:
    - programmation parallèle : plusieurs choses sont traitées à un moment donné physiquement parlant (mais du coup je fais de la programmation parallèle à petite échelle sur un CPU single core mais avec une pipeline superscalaire ? Vous avez 4h !)
    - programmation concurrente : plusieurs traitement logiciels sont menés de front, c'est donc orthogonal à la programmation parallèle (concurrent non parallèle si je fais tourner plusieurs threads logiques sur un CPU monocore)
    - programmation asynchrone : plusieurs traitement sont réalisé au sein d'un même flux de programmation (program flow).

    Du coup ça n'avance pas beaucoup, vu que je voudrais exprimer:
    1) programmation-ou-il-se-passe-plusieurs-choses-de-front: je parlais de programmation parallèle en m'abstrayant des questions physiques, mais il semblerait que c'est le terme concurrente qu'il faut employer ici (et du coup le terme parallèle ne sert à rien. vu qu'il dépend de l'architecture physique dont tout le monde se fout..)
    2) la même que 1) mais en précisant que l'implémentation se fait avec des threads et des locks comme briques de base: Encore une fois en s'arrêtant au kernel je trouve que le terme programmation concurrente marche bien ici (du point du vu de mon programme je vais effectivement avoir plusieurs threads en concurrence). J'imagine qu'il faut dire "programmation concurrente par threads" mais c'est long, et j'entends déjà le premier javaiste me demande où je classe les greenthreads...
    3) la même que 1) mais en précisant que l'implémentation se fait via sur un seul thread en le partageant de manière coopérative: J'imagine que programmation asynchrone marche bien ici

    Les exceptions sont déjà complexe à gérer là ça signifie que n'importe où une exception invisible peut subvenir ?

    C'est comme ça que Python fonctionne. Toutes les exceptions héritant de BaseException (genre SystemExit ou KeyboardInterrupt) sont déjà gérée comme ça. Note que ces exceptions ne sont pas invisible, les bonnes pratiques indiquent juste qu'il faut faire, sauf cas très particulier, un except Exception pour les laisser passer.

    L'énorme avantage de ce système c'est qu'il te suffit de faire un try/finally (ou bien d'utiliser un context manager) pour gérer le nettoyage de ton code (et ce quelque soit la cause: crash de ton code ou bien d'une coroutine sœur qui rend ton traitement caduque)

    Et enfin les exceptions son très peu cher en Python (enfin comparativement à la vitesse de Python bien sûr ), d'ailleurs c'est pour ça qu'elles sont déjà utilisées pour gérer des taches courantes comme les générators.

    La seule façon de survivre correctement à cela est d'avoir un code très défensif et perso ça ne me plaît pas des masses d'écrire ce genre de code.

    Dans tous les cas tu ne peux pas passer sous silence une erreur. L'intérêt d'une exception c'est que tu es obligé de la traiter (sinon ton programme crash), à l'inverse si tu gères tout ton système uniquement avec des passements de messages c'est très tentant de ne pas prendre en compte les crashes globales et de se retrouver avec un état incohérent au moment où ça arrive (typiquement des coroutines qui attendent indéfiniment la réponse de la coroutine ayant crashé).

    Je le répète: les problèmes d'init/teardown (en particulier dans le cas d'un crash) sont ce qu'il y a de plus compliqué en asynchrone, l'intérêt de trio est de te donner un contexte pour gérer ça simplement (on fonctionne avec des paradigmes très proche de ce qu'on ferait en programmation single thread classique). Ce que tu proposes à l'inverse c'est de gérer tout ça à la main via des passement de messages (je ne parle pas du traitement de l'erreur à proprement parler, mais de toute la machinerie qui permettra d'en arriver là), c'est du code complexe. C'est pour ça que erlang a OTP qui fait tout ce travaille pour toi (et dont la philosophie "let it crash" est très défensive de base).

    On parle de lever d'exception. Lever une exception c'est manipuler l'état de ton contexte.

    Au contraire, gérer une base de données par modification directe de l'état du système (ce que fait une levée d'exception) impose de gérer un lock global pour serialiser les opérations.

    L'exception ne modifie que le context de la coroutine touchée, c'est au développeur de choisir si la coroutine en question travaille sur toutes les données ou bien sur un sous ensemble isolé du reste du système.

    Si tu écris une méthode et qu'elle est finalement utilisée dans une tâche nursery, il sera impossible de voir dans le code de cette méthode qu'il manque potentiellement la gestion de cette dernière.

    Il n'y a pas de tâche nursery, tu as une coroutine parente qui veut lancer plusieurs traitements en parallèle. Pour ça elle créé un objet nursery qui permettra de borner le scope d'action des coroutines filles lancées.
    De faite si une fille crash, les autres filles sont arrêté proprement et l'exception d'origine est remontée dans la coroutine parente.
    Je te conseil cet article de l'auteur de trio (tous son blog vaut la peine d'être lu ) qui explique très bien l'intérêt de ce genre de mécanisme quand on cherche à gérer des timeouts.

    Toutes les bases de données ont déjà résolues se problème par la création d'un event log.

    Ou bien des locks à grain fin, ce que la programmation asynchrone rend beaucoup plus simple qu'en utilisant des threads classiques.
    De mémoire Redis fonctionne de cette façon (il implémente sa propre boucle asynchrone et lance une coroutine par connection client), on peut activer un journal de logs mais c'est pour rendre les données persistantes (donc dans tous les cas on se retrouve avec des coroutines qui travaillent sur les même données dans la base).
    Sinon un autre exemple (quoi que plus bas niveau) de ce type de besoin serait le Kernel Linux. Travailler par passement de messages est intéressant mais coûteux (cf. les débat kernel monolithique vs microkernel).

    Merci pour la discussion je la trouve enrichissante (ainsi qu'à Yves et Galndos).

    La même ;-)