Différences entre thread et async ?

Le problème exposé dans ce sujet a été résolu.

Bonjour amis zesteurs,

Je viens de lire quelques d'articles sur la programmation asynchrone (L'asynchrone et le multithread en .NET, Sortie de Python 3.5, La puissance cachée des coroutines…) et je n'arrive pas réellement à voir la différence avec le multi-threads (programmation concurrente selon Wikipédia si je ne me suis pas trompé).

Je sais que ces deux notions sont différentes mais c'est tout, je ne sais pas ou sont ces différences.

Si quelqu'un a des connaissances sur le sujet ou des ressources claire (l'anglais ne me dérange pas), je suis preneur. Même une simple liste avantages/inconvenients m'aiderait beaucoup :) .

+0 -0

Les threads sont une façon d'obtenir des programmes qui s'exécutent de façon concurrente. Les coroutines en sont une autre. Pour faire bonne mesure, on peut également mentionner le multi-processing.

Les différences sont :

  • Multi-processing: plusieurs processus qui s'exécutent en parallèle.

    • Pas de mémoire partagée entre les processus donc il faut mettre en place des outils de communication (IPC) parfois coûteux en performances, ou bien limités.
    • L'ordonnancement entre les processus est fait par le noyau du système d'exploitation, donc via des interruptions matérielles qui se produisent classiquement à chaque appel-système.
    • (En python et dans d'autres langages de la même famille) : c'est actuellement la seule solution pour paralléliser le calcul sur différents coeurs de traitement.
  • Threads: ce sont des fils d'exécution concurrents, natifs du système d'exploitation.

    • Plusieurs threads s'exécutent dans le même processus, donc la mémoire est partagée, et la communication entre les threads est très efficace (même si elle difficile à synchroniser et à rendre safe).
    • L'ordonnancement est réalisé comme pour les processus, par le noyau du système d'exploitation. C'est donc encore l'OS qui décide quand un thread doit s'arrêter et céder un peu de temps de calcul à un autre.
    • Dans les langages haut niveau comme Python qui sont sujets au GIL: le calcul reste astreint à (virtuellement) un seul coeur de traitement, donc c'est une solution qui vaut surtout le coup quand le programme réalise beaucoup plus d'IO (échanges réseau, lectures/écriture sur le disque, etc.) que de calcul, même s'il existe des solutions pour s'affranchir de cette limitation.
  • Les coroutines: mécanisme de thread léger qui repose bien souvent sur une boucle événementielle

    • Tout se passe dans le user-land, dans un seul processus, et même dans un seul thread. Le noyau de l'OS n'a rien à faire là-dedans, donc on évite, quelque part, de perdre du temps sur les appels système.
    • Quand le langage est pensé pour, le programmeur décide explicitement quand l'exécution d'un code va se mettre en attente d'un événement (et donc laisser les autres bosser).
    • C'est beaucoup plus facile à synchroniser que les threads et c'est utile dans le même cas de figure (beaucoup plus d'IO et d'attente que de calcul). Les canaux de communication sont explicites, et servent de points de synchro (les yield from ou await de Python, ou encore les channels de Go).
    • Inconvénient: tu ne peux pas prendre un code "normal" et le rendre asynchrone juste comme ça, un code asynchrone doit être pensé comme tel dès le départ.
+7 -0

Lu'!

Les primitives "async" sont de plus haut niveau que l'usage direct de threads. Elles permettent de ne pas s'occuper des questions de synchronisation ou de communication (à moins de volontairement se tirer une balle dans le pied). La majorité du temps, les compilateurs sont aussi libre de ne pas les implémenter avec des threads s'ils "jugent" leur usage trop coûteux.

Là ou avec un appel asynchrone, l'idée est simplement de dire "fait ça de manière asynchrone" puis "à partir de là j'attends le résultat", en multi-threading, tu devras poser tes variables de retour, lancer le thread, faire un join sur le thread, etc.

Merci nohar pour ta réponse rapide et complète.

Dans mon cas, se serait plutôt pour accélérer des calculs (dans le genre exploration de graphes, ex : A* ou minimax) plus que des IO, donc je devrais m'orienter vers du multi-processing si j'ai bien compris.

Merci Ksass`Peuk, mais du coup je suis plus sûr de savoir ce dont j'ai besoin ^^.

+0 -0

Il faut aussi que je mentionne que les "Goroutines" de Go mélangent un peu threads et asynchrone : elles ont l'avantage de requérir très peu de synchro, et celle-ci est explicite (le programme se met en attente quand on le décide), mais elles savent aussi s'exécuter en parallèle quand le runtime détecte qu'il a plusieurs coeurs à sa disposition.

En somme on peut dire que les threads sont un mécanisme système pour faire de la programmation concurrente (et parfois parallèle), alors que les coroutines sont une façon de formuler un programme concurrent, et les deux ne sont pas forcément mutuellement exclusifs.

+1 -0

Dans mon cas, se serait plutôt pour accélérer des calculs (dans le genre exploration de graphes, ex : A* ou minimax)

mrBen

Il me semble que l'exploration de graphe fait parti des problème particulièrement difficile à paralléliser. Il n'y a qu'à regarder les réponses google pour parallel graph analysis qui sont quasi exclusivement des papiers de recherche. Un des problème majeur est que pour paralléliser une tâche, il faut la diviser, ce qui n'est pas forcément faisable de manière efficace pour un graphe.

— nohar, c'est ce que j'avais cru comprendre lorsque je m'étais intéressé à ce langage (je n'ai jamais réussi à faire des Goroutines qui fonctionnent, mais je n'avais pas le même bagage technique que maintenant) !

— Berdes, mon problème n'était pas tant la difficulté qu'une grande confusion entre les différentes façons de faire.

Un des problème majeur est que pour paralléliser une tâche, il faut la diviser, ce qui n'est pas forcément faisable de manière efficace pour un graphe.

Berdes

Avec minimax, ça me semble plutôt facile : à chaque étape, on explore indépendamment chaque "enfant".

+0 -0

Pour le minimax classique et bas du front, en effet, mais si tu implémentes un élagage alpha-beta, tu gagnes probablement plus de temps de calcul qu'en parallélisant, avec un algo qui me semble difficilement parallélisable.

+1 -0

En effet, ça risque d'être compliqué de paralléliser avec un élagage alpha-beta ! Il faut que je teste.

Après, la question de départ était plus pour ma culture générale (et celle des visiteurs) que pour ce cas en particulier.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte