Journal La programmation concurrente en mode Goto

Posté par . Licence CC by-sa.
40
8
juin
2018

Bon, attention, il y a des gens qui se prennent pour Dijkstra dans ce journal.

À la base, je suis tombé sur cet article de Sam & Max qui parle de Trio. Jusque-là, on reste dans l'écosystème Python.

En fait, l'auteur de cette bibliothèque a écrit le fond de sa pensée : la programmation concurrente actuelle, qu'elle soit en coroutine, ou en thread pose les mêmes problèmes que l'utilisation du goto à ses débuts.

Vue schématique d'un bon vieux goto et d'un go (goroutine)

La lecture est longue mais intéressante. Parce que je suis convaincu, pour avoir utilisé asyncio que ça peut très vite ressembler à du code spaghetti.

Je ne peux pas vraiment juger de la solution apportée par Trio. Ça utilise les structures de contrôle de ressources (with … as en Python) pour s'assurer de la linéarité de l'exécution par rapport au code source. Ça fait évidemment plus propre à première vue. Et évidemment, ce n'est pas aussi souple que de lancer des tâches parallèles dans tous les coins, mais c'est justement le but : avoir une structure de code plus claire, et plus hiérarchique.

Et je sais que je vais profiter des commentaires intéressants, merci à vous !

  • # you make my day

    Posté par (page perso) . Évalué à 6.

    Suis abonné aux articles de sam&max …et celui là, je ne l'avais pas retenu (certainement le titre … goto -> au revoir ;-)
    Suis bien content que tu le ramènes ici (surtout un vendredi)

    ça m'a fait découvrir "trio". N'étant pas fan d'asyncio : suis ravi ;-)
    je vais potasser ça

  • # Pour complèter ...

    Posté par . Évalué à 10.

    Je vous invite à regarder cette série de vidéo sur le asyncio, asyn/await super bien fichue et qui aborde aussi les lib concurrentes ici:
    https://www.youtube.com/watch?v=ijxLs1D9Zjs&list=PL2CXLryTKuwza0Ln5i1tY3RvkadTwPkyF&index=3:

    Ca reprend le fond de l'article de Sam dont je n'aime définitivement pas la forme.

    La série asynchrone:
    https://www.youtube.com/watch?v=AjfSvp6Q-lM&index=1&list=PL2CXLryTKuwza0Ln5i1tY3RvkadTwPkyF

    Le cours complet:
    https://www.youtube.com/channel/UCIlUBOXnXjxdjmL_atU53kA

  • # Article de base excellent

    Posté par (page perso) . Évalué à 8.

    J'ai lu l'article de base de l'auteur de Trio il y a quelques jours et je pense qu'il va influencer ma pensée pour le reste de ma vie de codeur. Ce n'est pas tellement la présentation de Trio qui est importante (la lib est sûrement très bien, ce n'est pas la question) mais la réflexion de fond sur la conception des programmes.

    C'est très amusant de voir comment l'instruction GOTO est passé d'indispensable ("si on met des contraintes dessus on limite la conception") à inutile/dangereux (les structures de contrôle ajoutent certes des contraintes mais en fait apportent de nouvelles perspectives) ; et que pour la programmation concurrente on en est encore dans la phase 1 (on va naturellement penser que les contraintes vont être des limites).

    • [^] # Re: Article de base excellent

      Posté par . Évalué à 7.

      C'est toujours pareil, on rajoute de la sémantique, on explique plus ce que l'on veut faire et non comment le faire. Si le langage est assez riche, le traitement automatique (compilo, check) peut faire beaucoup plus de boulot. Si il est trop pauvre, on est bloqué pour faire certaines choses que l'on trouve élégante.

      "La première sécurité est la liberté"

      • [^] # Re: Article de base excellent

        Posté par (page perso) . Évalué à 3. Dernière modification le 08/06/18 à 14:07.

        Tout à fait. Mais après le diable se cache dans les détails ; sinon antre 2 langages avec la même approche (ex: impératif objet, fonctionnel pur…), celui plus haut niveau serait toujours intrinsèquement le meilleur. Mais pour des questions de performances notamment, ce n'est pas vraiment le cas [*]. L'article en parle lorsqu'il dit que break/continue on été ajoutés à l'idée de base des for/while ; et de même manière ce genre de compromis arrive dans sa propre bibliothèque.

        Donc on sait que dans l'absolu on peut toujours faire mieux en rajoutant de la sémantique ; mais il y a une infinité de façon de le faire et reste a trouver les meilleures façons (ce qui est donc difficile).

        [*] en pratique on va essayer quand même essayer d'écrire le plus possible dans le langage de plus haut niveau.

        • [^] # Re: Article de base excellent

          Posté par . Évalué à 4.

          Mais pour des questions de performances notamment, ce n'est pas vraiment le cas

          Dans l'exemple que tu donnes oui. Mais souvent, c'est une fausse bonne idée. Beaucoup d'optimisation devienne impossible (trie automatique des champs dans une structure en C, par exemple, "deforestation" quand les map/fold sont dans le langage (haskel),etc…)

          "La première sécurité est la liberté"

          • [^] # Re: Article de base excellent

            Posté par (page perso) . Évalué à 5.

            C'est bien le problème ; c'est souvent une mauvaise idée, mais pas toujours. Aussi il vaut mieux dans un premier temps rester sur quelque chose de haut niveau ; d'autant plus que les langages essaient de garder la compatibilité et donc enlever une fonctionnalité utilisée par plein de programmes n'est pas forcément possible (donc on doit garder une erreur de conception ad vitam). Mais à l'usage on voit bien que des entraves aux contraintes s'avèrent "nécessaires" (puisque les performances peuvent être une fonctionnalité).

            Par exemple en Rust les contraintes haut niveau amènent des garanties mémoires qu'on n'a pas en C, et certaines optimisations deviennent alors possibles (le compilateur C ne peut pas prendre certaines décisions optimisantes car il pourrait créer des bugs). Mais à côté de ça on a les blocs unsafe qui permettent dans de rares occasions de faire des optimisations qui seraient vues comme des erreurs par le compilateur. Il est évidemment conseillé de s'en servir le moins possible  ; et au final c'est surtout les libs de base (genre les collections) qui vont s'en servir, les programmes classiques s'en passent très bien.

            Dans l'absolu tous les langages ont ce genre de déverrouillage des contraintes, vu qu'ils permettent tous de s'interfacer avec du code C. C'est bien souvent pour pouvoir réutiliser du code (ça aurait été faisable dans le langage haut niveau, mais ça prendrait des ressources), mais c'est c'est souvent assumé qu'on va faire une partie critique au niveau performances en C (ou C++, Rust etc…).

          • [^] # Re: Article de base excellent

            Posté par . Évalué à 2.

            Tu peux expliquer ce que tu entends par '"deforestation" quand les map/fold sont dans le langage (haskel),etc…'?

            Je ne sais pas de quoi tu parles..

  • # Communauté et bibliothèques

    Posté par . Évalué à 7.

    La communauté Trio (dont je fais partie) est très accueillante : en plus de disposer d'un code de conduite, tous les contributeurs sont invités dans l'organisation Github, même pour des fautes de frappe dans la documentation.

    Le principal obstacle pour programmer en Trio aujourd'hui est l'absence de bibliothèques tierces. Il existe bien trio-asyncio, pour utiliser une bibliothèque asyncio dans un programme Trio. Ça fonctionne très bien, mais c'est une rustine en attendant d'avoir mieux : les problèmes inhérents à asyncio sont cachés mais existent encore.

    L'objectif n'étant pas de continuer à fragmenter l'écosystème des librairies asynchrone en Python, nous travaillons sur une approche de décoloration qui supporterait à la fois le Python 2/3 synchrone classique mais aussi les différentes solutions asynchrones existantes : Twisted, asyncio, Trio, etc. L'expérimentation se fait sur urllib3 dans le but d'ajouter le support de trio à requests. Si quelqu'un est intéressé, le développement se passe ici : https://github.com/python-trio/urllib3, voir https://github.com/urllib3/urllib3/pull/1335 pour une description de l'approche. Ce qui est enthousiasmant, c'est de voir que les mainteneurs de requests et pip sont intéressés, ce qui l'est un peu moins c'est qu'il reste beaucoup de travail ! :)

    • [^] # Re: Communauté et bibliothèques

      Posté par . Évalué à 2.

      en plus de disposer d'un code de conduite

      Ah ouf, je suis rassuré, j'avais vraiment besoin de ça pour y contribuer.

    • [^] # Re: Communauté et bibliothèques

      Posté par . Évalué à 4.

      En même temps, j'ai envie de poser la question : est-ce que la communauté « asynchrone » est si grande que ça dans l'écosystème Python ? Et même en général d'ailleurs. Il y a bien Javascript avec Node.js, et l'enfer du rappel que ça engendre (tiens, c'est une traduction rigolote du callback hell).

      Mais asyncio en Python est vraiment tout récent, voire carrément « bleeding edge » dans l'industrie. Peu de bibliothèques l'utilisent, j'ai l'impression.

      Et enfin, j'aurais envie de m'attarder sur le côté théorique plutôt que pratique. Trio propose un nouveau paradigme de programmation, une évolution. Ce n'est pas lié à Python, même si sa proposition concrète est faite avec. Mais qu'est-ce que ça peut donner dans d'autres langage ? Après tout, certains langages possèdent suffisamment de souplesse pour obtenir peu ou prou le même résultat.

      Évidemment, avant de généraliser, il vaut mieux montrer au monde que ça marche avec une implémentation solide et reconnue. En tout cas, c'est comme ça que ça fonctionne aujourd'hui.

      • [^] # Re: Communauté et bibliothèques

        Posté par (page perso) . Évalué à 3.

        est-ce que la communauté « asynchrone » est si grande que ça dans l'écosystème Python ?

        La programmation asynchrone c'est (quasi) 100 % des application Android, (quasi) 100 % des applications Windows, (quasi) 100 % des applications… heu ok, tout ce qui est avec interface graphique.
        Le callback hell se retrouve dans de très nombreux cas. Donc pas forcément hyper important pour le moment avec Python, mais le principe général est très bien (de mon très modeste point de vue, je ne suis pas développeur au quotidien).

        • [^] # Re: Communauté et bibliothèques

          Posté par (page perso) . Évalué à 3. Dernière modification le 11/06/18 à 13:50.

          Et enfin, j'aurais envie de m'attarder sur le côté théorique plutôt que pratique. Trio propose un nouveau paradigme de programmation, une évolution. Ce n'est pas lié à Python, même si sa proposition concrète est faite avec. Mais qu'est-ce que ça peut donner dans d'autres langage ? Après tout, certains langages possèdent suffisamment de souplesse pour obtenir peu ou prou le même résultat.

          Comme dit dans l'article de base, le concept appelé 'nursery' dans Trio n'est pas complètement nouveau (Trio apporte cependant quelques subtilités/variations), et se retrouve plus ou moins ailleurs. Il y a par exemple la lib Rayon pour Rust qui adopte un peu la même approche je crois.

          Edit: c'est au commentaire de Glandos que je voulais répondre.

          • [^] # Re: Communauté et bibliothèques

            Posté par . Évalué à 3.

            Il y a par exemple la lib Rayon pour Rust qui adopte un peu la même approche je crois.

            Ah oui, c'est pas mal. Bon, il y a des différences conceptuelles dans le contrat des primitives :

            If you do perform I/O, and that I/O should block (e.g., waiting for a network request), the overall performance may be poor.

            Donc, c'est un peu l'inverse de Trio sur ce coup-là. Ce qui peut-être vu comme un complément. Rayon a l'air de simplifier les tâches parallélisables en environnement préemptif, alors que Trio (comme asyncio) favorise les tâches en environnement coopératif.

            Mais ils ont l'immense honneur de mettre un terme au bordel, en instaurant une hiérarchie, permettant le retour des exceptions et de la bonne lecture du code.

      • [^] # Re: Communauté et bibliothèques

        Posté par . Évalué à 3.

        Concernant la taille de la communauté, c'est difficile à quantifier, mais quelle que soit sa taille, c'est contre-productif de ne pas partager le travail. On a la chance en Python de pouvoir implémenter asyncio et Trio dans des librairies, il ne faudrait pas que cette chance se retourne contre nous.

        Concernant l'industrie, tout ce que je peux dire c'est que je ne suis pas la seule personne a avoir écrit du code asyncio dans mon équipe, et on a du code Trio en production.

        Et enfin, sur le côté théorique, il y a différentes choses qui font que Trio avait de bonnes chances de naître en Python, mais les autres concepteurs de langages de programmation suivent avec intérêt. C'est difficile à montrer, mais ça donne par exemple des discussions un peu techniques sur ce qui différencie exactement les nurseries des autres techniques de programmation concurrente, comme https://twitter.com/graydon_pub/status/989249148288253954.

    • [^] # Re: Communauté et bibliothèques

      Posté par . Évalué à 3.

      Je comprends le concept recherché mais je ne connais pas du tout python, donc les exemples et les opérateurs me sont incompréhensibles.

      Pour avoir le concept se développer, il faut avoir une "api" générique. Tout le monde comprend le "if", le "while" ou la déclaration de fonction.

      Est-ce qu'il existe une doc de l'api non lié à python ?

      "La première sécurité est la liberté"

    • [^] # Re: Communauté et bibliothèques

      Posté par . Évalué à 3.

      Je me suis toujours demandé si un modèle de map() mais parallèle aurait du sens. L'idée est de faire comme la nursery mais avec synchronisation sur les données de sortie. En gros, le code de la branche principal tourne jusqu'à une tentative de lecture d'une sortie de la tache asynchrone. Ainsi, c'est le graphe de dépendance des variables qui fait la synchro des tâches (sender non-bloquant, receiver bloquant en communication par message).

      "La première sécurité est la liberté"

  • # question sur article sametmax

    Posté par . Évalué à 1.

    Je ne comprends pas une partie de l'article.

    Je cite :

    aio.ensure_future(foo()) # GOTO !
    Cette ligne ne garantit en aucun cas que foo() ou bar() seront terminées à une zone précise du code.

    Ma compréhension est que le paramètre passé à ensure_future(...) est le code retour de l'appel de fonction foo(). Donc foo() est forcément terminée dès le début d'exécution d'ensure_future().

    Ou bien je me trompe ?

    • [^] # Re: question sur article sametmax

      Posté par (page perso) . Évalué à 3.

      Ici foo est une fonction asynchrone, du coup appeler cette fonction ne provoque pas son exécution, mais créé un objet coroutine qui doit être passé à boucle d'évènements asynchrone (comme celle de asyncio ou bien de trio) pour être effectivement exécutée.

      C'est exactement le même comportement que ce qui tu aurais avec une fonction générateur (celle contenant le mot clé yield).

    • [^] # Re: question sur article sametmax

      Posté par . Évalué à 4.

      Effectivement, comme déjà dit, foo() est qualifiée par async.

      Un bon exemple (avec ipython):

      In [1]: async def foo():
         ...:     print('1')
         ...:     
      
      In [2]: def bar():
         ...:     print('2')
         ...:     
      
      In [3]: bar()
      2
      
      In [4]: foo()
      Out[4]: <coroutine object foo at 0x7fc5c6adae08>
      
      
  • # Je n'aime pas du tout cette article

    Posté par . Évalué à 5.

    Il est si bien écrit qu'il m'a fait passer énormément de temps à lire les autres articles sur le blog..

    Je n'utilise pas beaucoup python donc il est peu probable que je me serve de Trio mais nom de dieu qu'est ce que c'est bien conçu!
    Les articles ou il explique les problèmes et les solutions possibles et celle qu'il a choisi sont vraiment intéressants..

  • # Quelques réactions et... implémentation (existante de base !) en Kotlin

    Posté par . Évalué à 5.

    J'ai lu l'article, un peu méfiant (normal quand on lit quelqu'un qui se compare implicitement à Dijkstra ;)).

    Mes pensées :

    • ok, la programmation concurrente ça a toujours été bordélique, on se dit souvent qu'il y a sûrement quelque chose à améliorer ou un paradigme à inventer… donc pourquoi pas…
    • je n'avais jamais pensé à comparer les instructions "go" à des "goto", c'est une idée intéressante (mais est-elle pertinente ? (*)).
    • outre l'idéologie (la "boîte noire") et la beauté de la chose, on résout un problème clair : c'est que les exceptions remontent à l'"appelant", même celui-ci était dans un autre thread.

    (*) Tout dépend de ce qu'on veut inclure das les "boîtes noires".
    Considérant qu'une instruction "go" a juste 1 point d'entrée et 1 point de sortie dans le thread courant, celle-ci ne casse pas forcément l'abstraction "boîte noire". Tout dépend du fait qu'on considère la création de thread comme simple un effet de bord ou non. Là dessus, on peut aussi se dire qu'une bonne boîte noire ne devrait pas avoir d'effets de bord du tout (FP pure).

    Trêve de réflexions philosophiques, je me suis dit que cette idée devait être triviale à implémenter dans tout langage avec une syntaxe fonctionnelle bien conçue. En particulier, en Kotlin, en surcouche des coroutines. Donc j'ai essayé de le faire… pour me rendre compte assez vite que c'était déjà de base dans le framework… de base ou presque. Exemple :

    fun main(args: Array<String>) {
        runBlocking {
            launch(coroutineContext) {
                while (true) {
                    delay(1000)
                    println("coucou")
                }
            }
            launch(coroutineContext) {
                delay(3000)
                throw RuntimeException("Stop !")
            }
        }
    }

    (Ce programme quitte sur une exception au bout de 3 secondes. Celle-ci apparaît bien dans le thread main dans le backtrace.)

    Pourquoi presque ? Parce que ce n'est pas par défaut : il faut préciser le contexte coroutineContext pour avoir un comportement de type nursery. Si on ne le fait pas, la coroutine créée n'est pas enfant de la coroutine du scope extérieur, et donc une exception dans celle-là ne serait pas propagée à celle-ci.

    NJS ne prétendait pas forcément inventer une structure de contrôle complètement nouvelle, mais surtout encourager des langages qui n'autoriseraient la concurrence qu'au travers ce genre de structure. Donc ce "prior art" ne remet pas l'article en question.

    La conclusion, c'est qu'en Kotlin, avec une discipline assez légère (se forcer à n'utiliser que le contexte coroutineContext et ne pas utiliser d'autres primitives de concurrence que celles fournies dans les coroutines), on peut programmer dans le style suggéré par NJS (peut-être même pourrait-on ajouter un support dans l'IDE et dans le build system pour refuser tout programme qui ne s'y plie pas).

  • # Rx

    Posté par . Évalué à 6.

    Ouf… J'arrive un peu tard, mais j'étais loin de toute connexion ce week-end…

    J'avais lu l'article de sam&max, mais pas encore celui du développeur de trio. C'est intéressant, mais il répond à un problème que je n'ai pas personnellement.

    Lancer une série d'actions asynchrones et attendre qu'elles aient toutes finies, n'est pas quelque chose que je fais quotidiennement. En fait je ne le fais même pas du tout. Si j'en avais le besoins je pense que mon Rx (que j'essaie de privilégier dès que je fais de l'asynchrone) rempli ce rôle :

    Completable action1 = Completable.fromCallable(this::act1);
    Completable action2 = Completable.fromCallable(this::act2);
    Completable action3 = Completable.fromCallable(this::act3);
    
    Completable.concat(action1, action2, action3)
               .subscribe(() -> log.info("finish !"),
                          exception -> log.error("Error !", exception));

    Mais le comportement qui est proposé avec les nurseries me semble vraiment très risqué. Il mélange allègrement du comportement asynchrone et du comportement synchrone. Le switch de l'un des mondes à l'autre est (généralement) vraiment néfaste pour le comportement du programme. Bloquer le programme principal en attendant des tâches asynchrones c'est précisément ce que l'on ne veux pas faire. C'est plus une forme de parallélisme que de l'asynchrone.

    Il y a quelque chose que je n'ai pas compris dans le concept ou bien ?

    • [^] # Re: Rx

      Posté par (page perso) . Évalué à 4.

      Je crois effectivement que tu ne saisis pas bien le concept.

      Imagine un programme qui réalise les actions suivantes : A—B—C—E—G, et à l’étape B, il y a déclenchement d’une action parallèle dont le cheminement est D—F. Supposons maintenant que l’on s’attende à ce que cette branche parallèle soit terminée au moment où G se déroule : normalement, tu devrais programmer dans le thread principal une attente (passive) de la fin du thread secondaire. Si je comprends bien, avec Trio, tu as quelque chose comme ça :

      A
      B
      avec nursery(…) as n:
        n.lancer(C—E)
        n.lancer(D—F)
      G
      

      et automatiquement :

      • ça gère que C—E et D—F sont en parallèle
      • ça gère que si l’une des deux branches se plante, il faut interrompre l’autre
      • les exceptions se remontent toujours au thread principal
      • ça gère automatiquement le point de rendez-vous avant G

      C’est déjà pas mal.

      Maintenant, si tu n’as pas à proprement parler de notion de « tâches parallèles » mais juste le souhait d’avoir une tâche en arrière-plan, c’est pareil. Ainsi, si tu as le traitement A—B—C, où à B tu déclenches en arrière-plan la tâche de fond D, et que tu veux juste que tout ça fonctionne jusqu’à la fin du programme, tu obtiens ça :

      A
      B
      avec nursery(…) as n:
        n.lancer(C)
        n.lancer(D)
      

      et tu bénéficie toujours de tous les avantages sus-mentionnés (sauf le point de rendez-vous bien sûr).

      Là où Trio sera contre-productif, c’est si tu fork dans le but de laisser une tâche de fond qui continue au-delà de la fin du programme.

      • [^] # Re: Rx

        Posté par . Évalué à 2.

        C'est ce que fais le bout de code que j'ai décris plus haut. Les actions 1, 2 et 3 sont asynchrones (pas forcément parallèle) et tu place ton action G dans le subscribe. Si je reprends ta liste :

        1. ça gère que C—E et D—F sont en parallèle => c'est bien ce que je reproche c'est du parallélisme plus que de l'asynchrone. Le fait que ce soit parallèle ou pas est un détail pas un besoin fonctionnel.
        2. ça gère que si l’une des deux branches se plante, il faut interrompre l’autre => ouf ça veut dire quoi ? Je veux dire, il va préempter ton code et tout interrompre ? C'est particulièrement dangereux. Je vois pas bien à quoi ça sert et comment garantir qu'on retombe correctement sur nos pieds avec ça.
        3. les exceptions se remontent toujours au thread principal => c'est un besoin bizarre encore une fois, j'y reviens juste après
        4. ça gère automatiquement le point de rendez-vous avant G => c'est l'équivalent sur subscribe

        J'ai vraiment l'impression qu'il s'agit d'un pattern qui sert à ajouter un peu d'asynchrone dans un programme synchrone. Je ne connais pas les autres framework, mais quand tu fais du twist, tu es massivement asynchrone. Remonter dans le contexte principal à chaque fois que quelque chose se passe mal est intenable. Il faut réduire le scope de tes actions et chercher à porter les informations dans des messages plus que dans un état. La gestion d'un état dans un monde asynchrone est sincèrement dangereux parce qu'il est difficile de s'assurer qu'il reste cohérent quelque soit l'ordre dans le quel tes actions asynchrones se résolvent.

        • [^] # Re: Rx

          Posté par (page perso) . Évalué à 2.

          Je pense comprendre ce que tu veux dire… À mon avis, tu tombes dans le cas d’utilisation où cet outil n’est justement pas utilisable, le cas que je mentionnais à la fin. C’est par exemple le cas d’un listener sur un port réseau, qui délèguerait à un thread chaque nouvelle connexion : dans ce cas, une fois le thread lancé, on l’oublie, et on ne veut surtout pas :

          • ni gérer ses problèmes (exceptions) ;
          • ni pénaliser les connexions qui fonctionnent parce qu’il y en a une qui plante !

          Donc oui, Trio, c’est pour les bambins en liberté surveillée, pas pour les orphelins abandonnés :-p

          • [^] # Re: Rx

            Posté par . Évalué à 3.

            C’est par exemple le cas d’un listener sur un port réseau, qui délèguerait à un thread chaque nouvelle connexion

            C'est exactement pour ce cas là que l'asynchrone marche. Et justement, il ne faut pas faire de thread. Et il faut gérer les exceptions, pour éviter qu'un client ne pollue le programme complet.

            Voir http://www.kegel.com/c10k.html

            Bon après, il y a d'autres cas où oui, l'asynchrone ne marche pas bien, notamment quand les tâches sont complexes en calculs, et ne sont pas limitées par les entrées/sorties. Par exemple, décoder 16 flux vidéos de caméras en parallèle. Là, il faut clairement des threads, avec vaguement de temps en temps de la synchronisation.

            • [^] # Re: Rx

              Posté par . Évalué à 2.

              Pour les threads, pour embêter je dirais même que là où il y a des threads il n'y a pas d'asynchrone. C'est tellement caricatural que c'est limite faux, mais c'est pour mettre en évidence que c'est pas une question de thread et que dans le meilleur des cas on est asynchrone sans thread supplémentaire (merci les I/O asynchrones !).

              Et il faut gérer les exceptions, pour éviter qu'un client ne pollue le programme complet.

              Je ne suis pas certain de comprendre ta phrase, mais pour expliciter. Je pense que tu as en tête le pattern reactor et on ne veux clairement pas avoir de lever d'exception (pour toutes les raisons habituelles qui posent problèmes avec les exceptions).

          • [^] # Re: Rx

            Posté par . Évalué à 2.

            Donc oui, Trio, c’est pour les bambins en liberté surveillée, pas pour les orphelins abandonnés :-p

            Pas vraiment, l'idée c'est que les interactions doivent se faire par un passage de message que ton traitement asynchrone finisse ou plante ça émet un message qui va pouvoir être récupérer par une autre tâche pour y réagir. Il ne s'agit donc pas de ne pas les contrôler, mais d'avoir un pattern différent d'interaction.

            C'est ce tu trouvera dans les systèmes d'acteurs (voir erlang ou akka), avec le couple goroutines/channel de go ou avec node pour ne citer que les plus connus.

        • [^] # Re: Rx

          Posté par (page perso) . Évalué à 3.

          c'est bien ce que je reproche c'est du parallélisme plus que de l'asynchrone. Le fait que ce soit parallèle ou pas est un détail pas un besoin fonctionnel.

          Pour moi l'asynchrone est un mécanisme de programmation parallèle au même titre de la concurrence. Dans le premier cas (l'asynchrone) un seul traitement est réalisé à un moment donné contre plusieurs dans le second (et encore, à condition d'avoir plusieurs cœurs physiques…), mais ça c'est justement des détails et pas un besoin fonctionnel.
          À contrario parler de parallélisme c'est parler d'un besoin fonctionnel (exemple: j'ai besoin de faire tourner ma gui et mon code applicatif en parallèle)

          ça gère que si l’une des deux branches se plante, il faut interrompre l’autre => ouf ça veut dire quoi ? Je veux dire, il va préempter ton code et tout interrompre ? C'est particulièrement dangereux.

          Si une des deux branches se plante ça veut dire qu'on est dans un état qui mérite un traitement particulier, ça peut être arrêter toutes les coroutines jusqu'à un certain niveau, retenter de zéro la traitement de la coroutine ayant échoué et bien encore ne rien etc.
          L'important c'est surtout de se dire qu'il y a nécessairement besoin de faire quelque chose (ce qui peut être "ne rien faire" mais dans ce cas on le fait en connaissance de cause) et pas juste balancer une stacktrace dans stdout et faire comme si de rien n'était, ce qui est malheureusement la façon de faire de bien trop de frameworks parallèles.

          Ensuite effectivement une fois qu'on sait ce qu'on doit arrêter, encore faut-il le faire proprement. À ce niveau trio injecte une exception CancelledError dans la coroutine à terminer, ce qui permet à celle-ci de se nettoyer proprement, voir même d'annuler son arrêt si le cœur lui en dit (mais encore une fois ça dépend totalement du cas d'usage).

          mais quand tu fais du twist, tu es massivement asynchrone.

          j'imagine que tu parles de twisted ?

          Remonter dans le contexte principal à chaque fois que quelque chose se passe mal est intenable.

          Je n'ai pas compris ce que tu voulais dire par contexte principal ? Dans un framework asynchrone lambda une fois l'event loop lancée il n'y a pas une coroutine plus principale qu'une autre. Et de toute façon l'event loop en question passe déjà son temps à switcher d'une coroutine à une autre donc je ne vois pas en quoi aller dans un contexte supplémentaire serait intenable…

          Il faut réduire le scope de tes actions et chercher à porter les informations dans des messages plus que dans un état.

          C'est justement la force des nursery, cela te permet de créer des scope regroupant tes coroutines. Et donc De fait quand une coroutine plante ça remonte ton arbre de scopes (en arrêtant les coroutines en bonne ordre dans la foulée) jusqu'à ce que un try/except veuille bien s'en occuper. Si personne ne veut s'en occuper, ben ton programme crash. Et c'est bien normal, explicit is better than implicit comme ils disent.

          La gestion d'un état dans un monde asynchrone est sincèrement dangereux parce qu'il est difficile de s'assurer qu'il reste cohérent quelque soit l'ordre dans le quel tes actions asynchrones se résolvent.

          Tout à fait d'accord. Malgré tout c'est parfois difficile de travailler uniquement avec un système de messages (typiquement dans le cas d'une base de donnée qui doit pouvoir traiter plusieurs requêtes en parallèle alors que celles-ci travaillent sur les mêmes données).
          Dans ces cas là l'asynchrone apporte un gros avantage par rapport à la programmation concurrente puisque les points de concurrences sont explicites (i.e. tant qu'on ne met pas un await dans le code on a la garantie que personne ne va faire des choses dans notre dos). Évidemment cela n'est pas gratuit et a ses propres limitations (si la base de donnée de l'exemple précédent fait des traitements lourds en CPU par exemple)

          Et dans tous les cas, tu peux parfaitement utiliser trio pour faire du passement de messages, les nursery te permettent juste de mieux organiser ton système. Comme par exemple pour gérer proprement ce qui doit se passer quand un point de traitement de tes messages crash…

          • [^] # Re: Rx

            Posté par . Évalué à 3.

            Pour moi l'asynchrone est un mécanisme de programmation parallèle au même titre de la concurrence. Dans le premier cas (l'asynchrone) un seul traitement est réalisé à un moment donné contre plusieurs dans le second (et encore, à condition d'avoir plusieurs cœurs physiques…), mais ça c'est justement des détails et pas un besoin fonctionnel.

            Tu te contredis entre ta première et ta seconde phrase… L'asynchrone est un pattern qui découple temporellement une portion de code et celle qui l'a appelé. Le fait que ce soit parallèle n'est pas une obligation (c'est ce qui se passe quand on utilise POE par exemple).

            Si une des deux branches se plante ça veut dire qu'on est dans un état qui mérite un traitement particulier, ça peut être arrêter toutes les coroutines jusqu'à un certain niveau, retenter de zéro la traitement de la coroutine ayant échoué et bien encore ne rien etc.

            Ce genre de besoin devrait amha se gérer via des commandes. Tu crée des commandes et tu choisi leur comportement en cas de problème, leur timeout, etc. Ça permet de gérer les choses dans un contexte plus concis, tu ne viens pas demander à l'appelant de gérer cela.

            Ensuite effectivement une fois qu'on sait ce qu'on doit arrêter, encore faut-il le faire proprement. À ce niveau trio injecte une exception CancelledError dans la coroutine à terminer, ce qui permet à celle-ci de se nettoyer proprement, voir même d'annuler son arrêt si le cœur lui en dit (mais encore une fois ça dépend totalement du cas d'usage).

            Les exceptions sont déjà complexe à gérer là ça signifie que n'importe où une exception invisible peut subvenir ? Si ça intervient dans le code d'une bibliothèque ça se passe comment ? 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.

            j'imagine que tu parles de twisted ?

            Effectivement.

            Je n'ai pas compris ce que tu voulais dire par contexte principal ? Dans un framework asynchrone lambda une fois l'event loop lancée il n'y a pas une coroutine plus principale qu'une autre. Et de toute façon l'event loop en question passe déjà son temps à switcher d'une coroutine à une autre donc je ne vois pas en quoi aller dans un contexte supplémentaire serait intenable…

            On parle de lever d'exception. Lever une exception c'est manipuler l'état de ton contexte. La façon normale de remonter une erreur dans une event loop c'est de lui publier un évènement d'erreur. Le problème n'est pas le switch, mais le fait de remonter un comportement par une manipulation de l'état.

            C'est justement la force des nursery, cela te permet de créer des scope regroupant tes coroutines. Et donc De fait quand une coroutine plante ça remonte ton arbre de scopes (en arrêtant les coroutines en bonne ordre dans la foulée) jusqu'à ce que un try/except veuille bien s'en occuper. Si personne ne veut s'en occuper, ben ton programme crash. Et c'est bien normal, explicit is better than implicit comme ils disent.

            Cette remontée est une fuite de ton scope, là où un passage de message ferrait l'affaire. Quant à l'explicite, je ne pense pas qu'injecter une exception comme tu as l'air de le décrire soit explicite. 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.

            Tout à fait d'accord. Malgré tout c'est parfois difficile de travailler uniquement avec un système de messages (typiquement dans le cas d'une base de donnée qui doit pouvoir traiter plusieurs requêtes en parallèle alors que celles-ci travaillent sur les mêmes données).

            Toutes les bases de données ont déjà résolues se problème par la création d'un event log. Ça fige l'ordre des actions (et ça apporte un tas d'autres propriété sympa). Un event log c'est justement pour rester dans un système à message et ne manipuler un état que lorsque les évènements qui l'ont composés sont triés et rejouables. 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.

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

            • [^] # Re: Rx

              Posté par (page perso) . É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 ;-)

              • [^] # Re: Rx

                Posté par . Évalué à 1.

                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.

                Elles sont invisibles. Quelque chose qui n'est visible qu'au runtime est invisible c'est quelque chose qui peut arriver implicitement et ça rend presque impossible une validation par revue du code (c'est précisément ce que l'on recherche quand on dit qu'il faut être explicite). Je sais que ça fait partie du langage (on peut écrire ce que l'on veut dans les PEP, le monkey patching existe en python, c'est un langage dynamique ça autorise à faire un tas de choses de manière implicite). C'est aussi le cas de mon langage de prédilection (java), hein ? C'est juste que ça limite la manière dont je vais exprimer mon code asynchrone.

                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).

                Pourquoi ne pas gérer ça dans une forme de machine à état ? C'est ce que font les circuit breaker et les systèmes à acteurs (OTP ou akka le font pour toi), ça marche très bien et tu as la garantie que ton programme ne va pas exploser en vol à cause d'une erreur quelque part. Lancer une exception et carpe diem ce n'est pas sécurisé.

                « let it crash » c'est particulier. Ça dépend de ce que tu fais comme application. Si tu es dans un monde de microservice, avec un kubernetes qui te redémarre pourquoi pas (et encore…1) si tu fais une application mobile par exemple ce n'est pas envisageable.

                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.

                Interrompre son taf c'est déjà modifier son contexte, le fait de sortir manu military des blocs de codes détruits un tas de variables,… Imposer un try/except à tous les niveaux c'est difficilement tenable. Et ça empêche de continuer normalement son travail avant de gérer l'erreur.

                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).

                Redis n'est pas capable de gérer de multiples CPU et ne permet pas de véritablement gérer des écritures sur différents nœuds. Je ne suis pas certains qu'il fasse vraiment du lock à grain fin. Il fait du lock oui… Pour utiliser plusieurs CPU il faut faire du sharding de ton coté. À comparer avec hazelcast qui est une base de données comparable, mais architecturée en P2P avec un sharding automatique (ce qui permet de réduire la pression sur les event log - tu en crée un par shard). Tu peux aussi regarder du coté de cassandra qui fait précisément ça (aussi en P2P).

                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).

                Ah mais carrément ! Ça ne fonctionne pas pour tout. Le noyau a notamment besoin de débit et de latence délirante :

                • une latence sur les IRQ serait vraiment affreux pour notre utilisation
                • envoyer une image à la carte graphique par messages serait affreux

                Pour l'aspect sémantique c'est intéressant, mais j'ai plus le temps d'en parler tout de suite.


                1. ça va crasher toutes les requêtes en cours, tu as une centaine de requêtes en cours de traitement sur ton nœud tu as potentiellement 99 requêtes KO pour une en erreur… Pas cool. 

                • [^] # Re: Rx

                  Posté par (page perso) . Évalué à 2.

                  Quelque chose qui n'est visible qu'au runtime est invisible c'est quelque chose qui peut arriver implicitement et ça rend presque impossible une validation par revue du code

                  Les exceptions KeyboardInterrupt peuvent arriver à n'importe quelle moment dans ton programme Python et pourtant cela ne pose pas de soucis car on sait qu'il faut utiliser les block try/finally (ou les context manager font la même chose au final) pour gérer le nettoyage de ton code proprement. C'est pour cela que ce code est mauvais:

                  fd = open('foo', 'rw')
                  data = fd.read()
                  ... # si une exception raise fd.close() ne sera pas appelé
                  fd.write(result)
                  fd.close()
                  

                  et que celui-ci est bon:

                  with open('foo', 'rw') as fd:
                      data = fd.read()
                      ... # quoi qu'il arrive le with s'occupera de fermer fd
                      fd.write(result)
                  

                  C'est exactement la même chose avec trio, sauf que tu te prends une exception trio.Cancelled au lieu d'une KeyboardInterrupt (et ça te coute rien de plus puisque t'es déjà obligé de gérer le cas KeyboardInterrupt)

                  Pourquoi ne pas gérer ça dans une forme de machine à état ?

                  En effet si le language le permet c'est très bien de le faire. Toutefois ce n'est pas le cas de Python donc il faut soit recréer un framework pour le faire, soit se taper le code à la main (et là autant dire qu'on a perdu)… soit tout simplement utiliser un paradigme adapté à ce langage.

                  « let it crash » c'est particulier. Ça dépend de ce que tu fais comme application.

                  Ça dépend surtout du niveau de granularité que tu as mis en place. Si tu crashes tout appli et la relance de zéro c'est sûr que ça ne marchera pas de masse, mais il est tout à fait possible de ne relancer qu'une partie (exemple typique: un serveur web qui ne se relance pas à chaque erreur 500 qui lui arrive, au exemple: dans une appli mobile si le service responsable de multiplexer les requêtes vers ton backend crash tu le redémarres et tu as juste à signifier aux traitements qui était en attente de ce service qu'il y a eu une déconnexion)

                  Interrompre son taf c'est déjà modifier son contexte, le fait de sortir manu military des blocs de codes détruits un tas de variables,…

                  D'un autre côté son taf a déjà été interrompu par l'erreur à l'origine de l'exception, maintenant c'est juste question de retourner dans un état stable (et donc d'arrêter/relancer tout ce qui a besoin de l'être).

                  Au final j'ai l'impression que tu opposes programmation par message avec ce que propose trio.
                  De mon point trio est une brique de plus bas niveau qui te donne des outils de qualité (cad qui te font gagner du temps et qui te pousse à faire du code propre) pour faire de l'asynchrone, à charge pour toi de l'utiliser pour faire du passage de messages ou bien du partage de ressources en fonction de ton besoin.

              • [^] # Re: Rx

                Posté par . Évalué à 4.

                • programmation asynchrone : plusieurs traitement sont réalisé au sein d'un même flux de programmation (program flow).

                Ce ne serait pas plutôt l'idée qu'il est possible de faire des appels non-bloquants depuis le flow actuel pour lancer des traitements "en parallèle" (donc dans un autre flow ? Que faut-il appeler un flow ? Pour moi la notion était purement séquentielle !). Les résultats peuvent ensuite être attendus (par l'entremise d'un appel sur un futur ou une promesse).

                Cette définition cadre avec Wikipedia(en).

                Quant à la discussion sur la distinction entre parallélisme et concurrence. La différence se situerait effectivement dans le niveau d'abstraction.

                Je vais donner "mes" définitions succinctes :

                • parallélisme : exécution simultanée de plusieurs tâches (suppose l'existence de plusieurs unités d'exécution indépendantes). Le cas le plus pur étant l'exécution de tâches sur des processeurs physiquement distincts.
                • concurrence : exécution de front de tâches causalement indépendantes

                Considérant qu'une tâche peut consister à simuler une unité d'exécution (dans un paradigme plus haut niveau), plusieurs tâches indépendantes peuvent simuler des unités d'exécution indépendantes. Ainsi la concurrence au niveau d'abstraction n correspond au parallélisme du niveau n+1.

                Supposons qu'on programme avec l'API threads de son langage préféré. Alors 2 tâches s'exécutant sur des threads différents s'exécutent en parallèle du point de vue de l'API exposée. Pourtant, du point de vue de la machine, si elle est mono-core, l'exécution n'est que concurrente.

                Maintenant, si au lieu de programmer les tâches directement sur les threads, on les soumet plutôt à un ThreadPoolExecutor (Java) utilisant un seul thread, même du point de vue de l'API thread, l'exécution est seulement concurrente.

  • # apprendre les métiers du codage, développeur web, et

    Posté par . Évalué à 0.

    (jai posté la même chose sur un autre article mais finalement il serait plus juste de le poster aussi aussi dans l'espoir d'avoir de bons retours)

    Salut.

    Bon voilà c'est déçidé j'ai envie de sauter le pas, de faire quelque chose en rapport avec ce que j'ai presque toujours rêvé de faire : apprendre à programmer, apprendre le developpement web, apprendre à créer des application etc.

    Oui ça serait top mais comment ? Avec quoi ? Où ? Quand ?
    Je n'ai absolument aucunes références en la matière et non plus des acquis. J'ai toujours aimé l'informatique et les nouvelles technologies, ça me passionne. Je suis celui à qui les voisins, les copains, les amis, mon entourage font appels quand ils ont besoin de réparer leurs PC, de virer des virus, d'avoir une aide en informatique. Mais mes compétences s'arrêtent là donc autant dire à rien. J'ai envie d'aller plus loin et à mon âge je n'ai plus de temps à perdre ou je le regretterais le restant de ma vie d'merde. Désolé mais je l'admet ma vie n'est pas très joyeuse et je connais une longue période de chômage bien malgré moi. Maudit Conseiller d'Orientation à l'époque du lycée qui m'oblogeait à m'orienter vers les métiers du bâtiment comme dans l'électrotechnique (entre autres), comme si on était pas capable de faire d'autres métiers car à cette période les mecs issus de l'immigration c'était vers la seule chose dans laquelle les profs et les conseillers d'Orientation nous orientaient et non vers des métiers qu'on rêvait réellement de faire, c'est-à-dire dan smon cas en rapport avec l'informatique. Je me souviens encore quand je disais que je voulais devenir programmeur, informaticien, et j'en passe et qu'on me répondait "non, ça ne sera pas possible, il faut vous orienter vers les métiers du bâtiment M'sieur B…….". D'entrée ils nous programmaient et nous rabaissaient comme si on était pas capable de viser mieux, plus haut. Et je n'exagère pas. Et au final ben je me suis retrouvé à faire des trucs que je détestais profondément et insidieusement on nous lobotomisaient pour aller vers des métiers comme ceux que nos parents, bien que je ne considère pas ces métiers comme humiliant bien au contraire ce sont de beaux métiers mais ma génération était constamment tirée vers la bas pour pas qu'on s'oriente ver slà où on rêvait d'aller. La génération sacrifiée quoi, ça parle peut-être à certain ?

    Bref, donc voilà il est temps d'agir, s'il n'est pas trop tard pour m'orienter vers ces métiers là même à mon âge (purée le temps est passée trop vite, mais bon sang il s'est passé quoi là ?!)
    J'aimerais vos conseils avisés. Je rêve d'apprendre mais je ne sais pas par où ni par quoi commencer. Je n'ai pas l'argent pour me payer des formations coûteuses. Alors quelles options ai-je ? Vers qui ou quoi me tourner quitte à être autodidacte s'il le faut ?
    Je regrette toutes ces années de perdues et maintenant je veux changer ça si toutefois il est encore temps d'agir.

    Donc pour récapituler, pour apprendre a développer des applications sur android et ios, devenir développeur web, la programmation, etc quand on a pas l'argent pour suivre une formation et ouvert à tout âge (d'ailleurs y'a t-il un âge pour débuer ou non ?) :

    *Par quoi commencer ?
    Où et comment ?
    Sur quel système d'exploitation ? (puis le faire avec un PC normal sous OS Windows) ?
    En combien de temps puis-je espérer apprendre, étant donné que j'ai beaucoup de temps de disponible ?
    Est-ce très compliqué ?
    Faut-il des compétences particulières et nécessaires pour apprendre ?
    Et le tout quand on a pas un kopec en poche et qu'on a pas le BAC ?
    *
    J'espère que vous pourrez m'aider et m'orienter. J'habite Toulouse pour info.

    Peace.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.