Question

Que sont-ils et à quoi servent-ils ?

Je n'ai pas de diplôme CS et mon parcours est VB6 -> ASP -> ASP.NET/C#.Quelqu'un peut-il l'expliquer de manière claire et concise ?

Était-ce utile?

La solution

Imaginez si chaque ligne de votre programme était une fonction distincte.Chacun accepte, en paramètre, la prochaine ligne/fonction à exécuter.

En utilisant ce modèle, vous pouvez "mettre en pause" l'exécution sur n'importe quelle ligne et la poursuivre plus tard.Vous pouvez également faire des choses inventives, comme remonter temporairement la pile d'exécution pour récupérer une valeur, ou enregistrer l'état d'exécution actuel dans une base de données pour le récupérer ultérieurement.

Autres conseils

Vous les comprenez probablement mieux que vous ne le pensez.

Les exceptions sont un exemple de continuations « vers le haut uniquement ».Ils permettent au code situé au fond de la pile d'appeler un gestionnaire d'exceptions pour indiquer un problème.

Exemple Python :

try:
    broken_function()
except SomeException:
    # jump to here
    pass

def broken_function():
    raise SomeException() # go back up the stack
    # stuff that won't be evaluated

Les générateurs sont des exemples de continuations « vers le bas uniquement ».Ils permettent au code de rentrer dans une boucle, par exemple pour créer de nouvelles valeurs.

Exemple Python :

def sequence_generator(i=1):
    while True:
        yield i  # "return" this value, and come back here for the next
        i = i + 1

g = sequence_generator()
while True:
    print g.next()

Dans les deux cas, ceux-ci ont dû être ajoutés spécifiquement au langage alors que dans un langage avec des continuations, le programmeur peut créer ces éléments là où ils ne sont pas disponibles.

Attention, cet exemple n’est ni concis ni exceptionnellement clair.Il s'agit d'une démonstration d'une application puissante des continuations.En tant que programmeur VB/ASP/C#, vous n'êtes peut-être pas familier avec le concept de pile système ou d'état de sauvegarde, le but de cette réponse est donc une démonstration et non une explication.

Les continuations sont extrêmement polyvalentes et constituent un moyen de sauvegarder l’état d’exécution et de le reprendre plus tard.Voici un petit exemple d'environnement multithread coopératif utilisant des continuations dans Scheme :

(Supposons que les opérations de mise en file d'attente et de retrait de la file d'attente fonctionnent comme prévu sur une file d'attente globale non définie ici)

(define (fork)
  (display "forking\n")
  (call-with-current-continuation
   (lambda (cc)
     (enqueue (lambda ()
                (cc #f)))
     (cc #t))))

(define (context-switch)
  (display "context switching\n")
  (call-with-current-continuation
   (lambda (cc)
     (enqueue
      (lambda ()
        (cc 'nothing)))
     ((dequeue)))))

(define (end-process)
  (display "ending process\n")
  (let ((proc (dequeue)))
    (if (eq? proc 'queue-empty)
        (display "all processes terminated\n")
        (proc))))

Cela fournit trois verbes qu'une fonction peut utiliser : fork, context-switch et end-process.L'opération fork divise le thread et renvoie #t dans une instance et #f dans une autre.L'opération de changement de contexte bascule entre les threads et le processus de fin termine un thread.

Voici un exemple de leur utilisation :

(define (test-cs)
  (display "entering test\n")
  (cond
    ((fork) (cond
              ((fork) (display "process 1\n")
                      (context-switch)
                      (display "process 1 again\n"))
              (else (display "process 2\n")
                    (end-process)
                    (display "you shouldn't see this (2)"))))
    (else (cond ((fork) (display "process 3\n")
                        (display "process 3 again\n")
                        (context-switch))
                (else (display "process 4\n")))))
  (context-switch)
  (display "ending process\n")
  (end-process)
  (display "process ended (should only see this once)\n"))

La sortie devrait être

entering test
forking
forking
process 1
context switching
forking
process 3
process 3 again
context switching
process 2
ending process
process 1 again
context switching
process 4
context switching
context switching
ending process
ending process
ending process
ending process
ending process
ending process
all processes terminated
process ended (should only see this once)

Ceux qui ont étudié le forking et le threading dans un cours reçoivent souvent des exemples similaires à celui-ci.Le but de cet article est de démontrer qu'avec les continuations, vous pouvez obtenir des résultats similaires au sein d'un seul thread en enregistrant et en restaurant son état - sa continuation - manuellement.

P.S.- Je pense que je me souviens de quelque chose de similaire dans On Lisp, donc si vous souhaitez voir du code professionnel, vous devriez consulter le livre.

Une façon de considérer une continuation est de la considérer comme une pile de processeurs.Lorsque vous "appelez avec la continuation actuelle c", il appelle votre fonction "c", et le paramètre passé à "c" est votre pile actuelle avec toutes vos variables automatiques dessus (représentée comme encore une autre fonction, appelez-la "k ").Pendant ce temps, le processeur commence à créer une nouvelle pile.Lorsque vous appelez "k", il exécute une instruction "retour de sous-programme" (RTS) sur la pile d'origine, vous ramenant au contexte de "l'appel avec continuation actuelle" d'origine ("call-cc" à partir de maintenant allumé) et permettant à votre programme de continuer comme avant.Si vous avez passé un paramètre à "k", cela devient la valeur de retour de "call-cc".

Du point de vue de votre pile d'origine, le "call-cc" ressemble à un appel de fonction normal.Du point de vue de "c", votre pile d'origine ressemble à une fonction qui ne revient jamais.

Il y a une vieille blague à propos d'un mathématicien qui a capturé un lion dans une cage en grimpant dans la cage, en la verrouillant et en déclarant qu'il était à l'extérieur de la cage alors que tout le reste (y compris le lion) était à l'intérieur.Les suites, c'est un peu la cage, et le "c" c'est un peu le mathématicien.Votre programme principal pense que "c" est à l'intérieur, tandis que "c" pense que votre programme principal est à l'intérieur de "k".

Vous pouvez créer des structures de flux de contrôle arbitraires à l'aide de continuations.Par exemple, vous pouvez créer une bibliothèque de threads."yield" utilise "call-cc" pour mettre la suite actuelle dans une file d'attente, puis passe à celle en tête de la file d'attente.Un sémaphore possède également sa propre file d'attente de continuations suspendues, et un thread est replanifié en le retirant de la file d'attente du sémaphore et en le plaçant dans la file d'attente principale.

Fondamentalement, une continuation est la possibilité pour une fonction d’arrêter son exécution, puis de reprendre là où elle s’était arrêtée ultérieurement.En C#, vous pouvez le faire en utilisant le mot clé rendement.Je peux entrer plus en détail si vous le souhaitez, mais vous vouliez une explication concise.;-)

Je suis encore en train de m'habituer aux continuations, mais une façon d'y penser que je trouve utile est de les considérer comme des abstractions du concept Program Counter (PC).Un PC "pointe" vers la prochaine instruction à exécuter en mémoire, mais bien sûr, cette instruction (et à peu près toutes les instructions) pointe, implicitement ou explicitement, vers l'instruction qui suit, ainsi que vers les instructions qui doivent interrompre le service.(Même une instruction NOOP effectue implicitement un JUMP vers l'instruction suivante en mémoire.Mais si une interruption se produit, cela impliquera généralement un JUMP vers une autre instruction en mémoire.)

Chaque point potentiellement « actif » d'un programme en mémoire vers lequel le contrôle peut sauter à un moment donné est, en un sens, une continuation active.D'autres points qui peuvent être atteints sont des continuations potentiellement actives, mais, plus précisément, ce sont des continuations qui sont potentiellement « calculées » (dynamiquement, peut-être) suite à l'atteinte d'une ou plusieurs des continuations actuellement actives.

Cela semble un peu déplacé dans les introductions traditionnelles aux continuations, dans lesquelles tous les threads d'exécution en attente sont explicitement représentés comme des continuations dans du code statique ;mais il prend en compte le fait que, sur les ordinateurs à usage général, le PC pointe vers une séquence d'instructions qui pourrait potentiellement modifier le contenu de la mémoire représentant une partie de cette séquence d'instructions, créant ainsi essentiellement une nouvelle (ou modifiée, si vous voulez). ) suite à la volée, celle qui n'existe pas vraiment dès les activations de suites précédant cette création/modification.

Ainsi, la continuation peut être considérée comme un modèle de haut niveau du PC, c'est pourquoi elle englobe conceptuellement l'appel/retour de procédure ordinaire (tout comme le fer ancien faisait l'appel/retour de procédure via JUMP de bas niveau, alias GOTO, les instructions plus l'enregistrement du PC en appel et restauration de celui-ci au retour) ainsi que les exceptions, threads, coroutines, etc.

Ainsi, tout comme le PC indique que des calculs auront lieu dans « le futur », une continuation fait la même chose, mais à un niveau plus élevé et plus abstrait.Le PC fait implicitement référence à la mémoire ainsi qu'à tous les emplacements de mémoire et aux registres « liés » à toutes les valeurs, tandis qu'une continuation représente l'avenir via les abstractions appropriées au langage.

Bien sûr, même s'il n'y a généralement qu'un seul PC par ordinateur (processeur central), il existe en fait de nombreuses entités PC « actives », comme évoqué ci-dessus.Le vecteur d'interruption en contient un tas, la pile un tas plus, certains registres peuvent en contenir, etc.Ils sont "activés" lorsque leurs valeurs sont chargées dans le PC matériel, mais les continuations sont des abstractions du concept, pas des PC ou leur équivalent précis (il n'y a pas de concept inhérent de continuation "maître", même si nous pensons et codons souvent en ces termes pour garder les choses assez simples).

Essentiellement, une continuation est une représentation de « que faire ensuite lorsqu'elle est invoquée » et, en tant que telle, peut être (et, dans certains langages et dans les programmes de type transmission de continuation, c'est souvent) un objet de première classe qui est instancié, transmis et supprimé comme la plupart des autres types de données, et un peu comme la façon dont un ordinateur classique traite les emplacements de mémoire vis-à-vis le PC - comme presque interchangeable avec les entiers ordinaires.

En C#, vous avez accès à deux suites.Un, accessible via return, permet à une méthode de continuer là où elle a été appelée.L'autre, accessible via throw, permet à une méthode de continuer à la correspondance la plus proche catch.

Certains langages vous permettent de traiter ces instructions comme des valeurs de première classe, vous pouvez donc les attribuer et les transmettre dans des variables.Cela signifie que vous pouvez cacher la valeur de return ou de throw et appelle-les plus tard quand tu seras vraiment prêt à retourner ou à jeter.

Continuation callback = return;
callMeLater(callback);

Cela peut être utile dans de nombreuses situations.Un exemple est comme celui ci-dessus, où vous souhaitez suspendre le travail que vous effectuez et le reprendre plus tard lorsque quelque chose se produit (comme recevoir une requête Web ou quelque chose du genre).

Je les utilise dans quelques projets sur lesquels je travaille.Dans l'un d'entre eux, je les utilise pour pouvoir suspendre le programme pendant que j'attends les E/S sur le réseau, puis le reprendre plus tard.Dans l'autre, j'écris un langage de programmation dans lequel je donne à l'utilisateur accès aux continuations en tant que valeurs afin qu'il puisse écrire return et throw pour eux-mêmes - ou tout autre flux de contrôle, comme while boucles - sans que j'aie besoin de le faire à leur place.

Pensez aux fils.Un thread peut être exécuté et vous pouvez obtenir le résultat de son calcul.Une continuation est un fil de discussion que vous pouvez copier afin de pouvoir exécuter deux fois le même calcul.

Les continuations ont suscité un regain d'intérêt pour la programmation Web car elles reflètent joliment le caractère pause/reprise des requêtes Web.Un serveur peut construire une continuité représentant la session d'un utilisateur et reprendre si et quand l'utilisateur continue la session.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top