Une boucle d'événement est-elle une boucle for / while avec une scrutation optimisée?

55

J'essaie de comprendre ce qu'est une boucle d'événements. Souvent, l'explication est que dans une boucle d'événement, vous faites quelque chose jusqu'à ce que vous soyez averti qu'un événement s'est produit. Vous gérez ensuite l'événement et continuez à faire ce que vous faisiez auparavant.

Mapper la définition ci-dessus avec un exemple. J'ai un serveur qui "écoute" dans une boucle d'événement et quand une connexion de socket est détectée, les données de celle-ci sont lues et affichées, après quoi le serveur reprend / commence à écouter comme il le faisait auparavant.


Cependant, cet événement se produit et nous nous informons «comme ça» est trop difficile à gérer pour moi. Vous pouvez dire: "Ce n'est pas" juste comme ça ", vous devez enregistrer un auditeur d'événement". Mais qu'est-ce qu'un écouteur d'événement mais une fonction qui, pour une raison quelconque, ne revient pas? Est-il dans sa propre boucle, attendant d'être averti lorsqu'un événement se produit? L'auditeur d'événement doit-il également enregistrer un auditeur d'événement? Où finit-il?


Les événements sont une abstraction agréable à utiliser, bien qu’une abstraction. Je pense qu’en fin de compte, le scrutin est inévitable. Peut-être que nous ne le faisons pas dans notre code, mais les niveaux inférieurs (l’implémentation du langage de programmation ou le système d’exploitation) le font pour nous.

Cela revient essentiellement au pseudo-code suivant qui tourne assez bas pour ne pas attendre en attente:

while(True):
    do stuff
    check if event has happened (poll)
    do other stuff

C’est ce que je comprends de l’idée et je voudrais savoir si c’est correct. Je suis ouvert à accepter que toute cette idée soit fondamentalement fausse, auquel cas j'aimerais une explication correcte.

L'ingénieur significatif
la source
3
Les systèmes d'événements sont des implémentations du modèle Observer . Comprendre le modèle devrait cimenter votre compréhension des événements. Aucun scrutin n'est requis.
Steven Evers
1
En fin de compte oui, même si les constructions de langage l'abstruisent.
GrandmasterB
@SteveEvers Dans votre lien wiki. Que EventSourcefait-on si on n’interroge pas l’entrée au clavier?
TheMeaningfulEngineer
@Alan: Cela pourrait faire n'importe quoi, mais pour la saisie au clavier, il existe des API pour enregistrer votre application en tant qu'écoute d'événements de clavier, que vous pouvez ensuite utiliser comme source d'événements sans interrogation. Bien sûr, si vous utilisez suffisamment d’ ondes, l’interrogation USB est toujours active, mais supposons que nous utilisons un clavier PS / 2 piloté par interruptions. Vous disposez alors d’une pile d’entrée clavier basée sur des événements sans interrogation.
Phoshi
J'ai cette question depuis des années, mais je ne peux pas dire pourquoi je ne me suis jamais donné la peine de la poser. Merci, je suis maintenant satisfait de la compréhension que @Karl Bielefeldt m’a éclairée.
0xc0de

Réponses:

54

La plupart des boucles d'événement seront suspendues si aucun événement n'est prêt, ce qui signifie que le système d'exploitation ne donnera à la tâche aucun délai d'exécution tant qu'un événement ne se produit pas.

Dites que l'événement est une touche enfoncée. Vous pourriez vous demander s'il y a une boucle quelque part dans le système d'exploitation qui recherche les touches du clavier. La réponse est non. Les touches enfoncées génèrent une interruption , gérée de manière asynchrone par le matériel. De même pour les minuteries, les mouvements de souris, l'arrivée d'un paquet, etc.

En fait, pour la plupart des systèmes d'exploitation, l'interrogation d'événements est l'abstraction. Le matériel et le système d'exploitation traitent les événements de manière asynchrone et les placent dans une file d'attente pouvant être interrogée par les applications. Dans les systèmes embarqués, on ne voit vraiment que de véritables interrogations au niveau du matériel, même pas toujours.

Karl Bielefeldt
la source
4
Une interruption n'est-elle pas un changement de tension sur un fil? Cela peut-il déclencher un événement seul ou devons-nous interroger la broche pour obtenir la valeur de la tension?
TheMeaningfulEngineer
8
Cela peut déclencher un événement en soi. Le processeur est conçu de cette façon. En fait, un processeur peut être réveillé du sommeil par une interruption.
Karl Bielefeldt
2
Je suis confus avec la déclaration Most event loops will block. Comment cela s'intègre-t-il dans le "paradigme de la boucle d'événement, opposé à l'utilisation de threads, utilisant des appels asynchrones non bloquants"?
TheMeaningfulEngineer
1
Si vous examinez les boucles d'événements pour une bibliothèque telle que GTK +, ils recherchent de nouveaux événements, puis appellent les gestionnaires d'événements dans une boucle, mais en l'absence d'événements, ils bloquent un sémaphore, une minuterie ou quelque chose du genre. Les développeurs individuels créent leurs propres boucles d’événements qui ne bloquent pas sur une file d’événements vide, mais bloquent toutes les bibliothèques largement utilisées. Les boucles d'événements sont trop inefficaces sinon.
Karl Bielefeldt
1
Je suppose que vous pourriez voir les choses sous cet angle, mais il s’agit d’un multi-threading dans la bibliothèque et / ou le système d’exploitation plutôt que dans le code de l’application. Les boucles d'événements résument les problèmes de synchronisation pour le développeur de l'application.
Karl Bielefeldt
13

Je pense à un auditeur d'événement non pas à une fonction exécutant sa propre boucle, mais à une course de relais avec le premier coureur attendant le coup d'envoi. Une raison importante pour utiliser des événements plutôt que des interrogations est qu’ils sont plus efficaces avec les cycles du processeur. Pourquoi? Regardez-le à partir du matériel (plutôt que du code source).

Considérons un serveur Web. Lorsque votre serveur appelle listen()et bloque, votre code prend sa place en tant que coureur de relais. Lorsque le premier paquet d'une nouvelle connexion arrive, la carte réseau commence la course en interrompant le système d'exploitation. Le système d'exploitation exécute une routine de service d'interruption (ISR) qui récupère le paquet. L'ISR passe le témoin à une routine de niveau supérieur qui établit la connexion. Une fois la connexion établie, cette routine passe le témoin à listen(), qui le transmet ensuite à votre code. À ce stade, vous pouvez faire ce que vous voulez avec la connexion. Tout ce que nous savons, entre les courses, chaque coureur pourrait être envoyé au pub. Une des forces de l’abstraction d’événement est que votre code n’a pas besoin de connaître ni de s’inquiéter.

Certains systèmes d'exploitation incluent un code de gestion d'événement qui exécute sa partie de la course, passe le relais de la main, puis revient au point de départ pour attendre le début de la course. En ce sens, la gestion des événements est une interrogation optimisée dans de nombreuses boucles simultanées. Cependant, il existe toujours un déclencheur extérieur qui déclenche le processus. L'écouteur d'événements n'est pas une fonction qui ne retourne pas, mais une fonction qui attend ce déclencheur externe avant son exécution. Plutôt que:

while(True):
    do stuff
    check if event has happened (poll)
    do other stuff

Je pense à cela comme:

on(some event):    //I got the baton
     do stuff
     signal the next level up    //Pass the baton

et entre le signalet la prochaine fois que le gestionnaire s'exécute, il n'y a théoriquement aucun code en cours d'exécution ou en boucle.

cxw
la source
Cela a du sens, mais que fait la boucle d’événements en attendant l’interruption? Si cela bloque, n’est-ce pas l’approche «multithread» qui est le paradigme opposé à la boucle d’événement?
TheMeaningfulEngineer
Si votre code contient la boucle, forever: { select(); do stuff; }vous entrez à nouveau dans la course à chaque fois. Que vous fassiez cela à plusieurs reprises à partir d'un seul thread ou en parallèle sur des threads ou des processeurs distincts, je considère chaque événement comme sa propre course. Par exemple, un navigateur Web est un programme multithread avec plusieurs boucles d'événements dans des threads distincts, au moins une pour l'interface utilisateur et une pour chaque page téléchargée. La question que je pose lors du codage est "Comment puis-je traiter les événements assez rapidement?" Parfois, la réponse est une boucle, parfois des threads, souvent une combinaison.
cxw
Je fais de la programmation événementielle depuis plus de deux décennies et j’ai trouvé cette réponse très déroutante. Pour qu'un auditeur fonctionne, il doit exister une boucle qui attend un événement, puis l'achemine vers tous les écouteurs enregistrés. (En supposant que nous parlons de logiciels plutôt que d'interruptions matérielles)
Bryan Oakley
8

Non, il ne s'agit pas d'une "interrogation optimisée". Une boucle d'événement utilise des E / S basées sur des interruptions au lieu d'interrogation.

Les boucles While, Until, For, etc. sont des boucles d'interrogation.

"Polling" est le processus de vérification répétée de quelque chose. Étant donné que le code de boucle en continu exécute, et parce qu'il est une petite boucle « serré », il y a peu de temps pour le processeur à des tâches de commutation et faire autre chose. Presque tous les "blocages", "gels", "blocages" ou ce que vous voulez appeler lorsque l'ordinateur ne répond plus, sont la manifestation du code bloqué dans une boucle d'interrogation non intentionnelle. Instrumentation affichera 100% d'utilisation du processeur.

Les boucles d'événements déclenchées par une interruption sont beaucoup plus efficaces que les boucles d'interrogation. L’interrogation est une utilisation extrêmement fastidieuse des cycles de la CPU et tout est mis en œuvre pour l’éliminer ou la minimiser.

Cependant, pour optimiser la qualité du code, la plupart des langages tentent d'utiliser le paradigme de la boucle d'interrogation aussi étroitement que possible pour les commandes de traitement des événements, dans la mesure où ils remplissent des fonctions similaires au sein d'un programme. Ainsi, l'interrogation étant le moyen le plus familier d'attendre un appui sur une touche ou quelque chose du genre, il est facile pour les inexpérimentés de l'utiliser et de se retrouver avec un programme qui peut fonctionner correctement tout seul, mais rien d'autre ne fonctionne pendant son exécution. Il a "repris" la machine.

Comme expliqué dans d'autres réponses, dans la gestion d'événements déclenchés par une interruption, un "indicateur" est défini dans la CPU et le processus est "suspendu" (non autorisé à s'exécuter) jusqu'à ce que cet indicateur soit modifié par un autre processus (tel que le clavier). conducteur le changeant lorsque l'utilisateur a appuyé sur une touche). Si l'indicateur est une condition matérielle réelle telle qu'une ligne "tirée haut", cela s'appelle une "interruption" ou une "interruption matérielle". Cependant, la plupart ne sont implémentées que comme une adresse de mémoire sur la CPU ou dans la mémoire principale (RAM) et sont appelées "sémaphores".

Les sémaphores peuvent être modifiés sous contrôle logiciel et peuvent donc fournir un mécanisme de signalisation très rapide et simple entre les processus logiciels.

Les interruptions, cependant, ne peuvent être modifiées que par le matériel. L’utilisation la plus répandue des interruptions est celle déclenchée à intervalles réguliers par la puce d’horloge interne. Le changement de sémaphores est l’un des innombrables types d’actions logicielles activées par les interruptions d’horloge.

J'ai beaucoup laissé de côté mais j'ai dû m'arrêter quelque part. S'il vous plaît demander si vous avez besoin de plus de détails.

DocSalvager
la source
7

Généralement, la réponse est matérielle, le système d'exploitation et les threads d'arrière-plan que vous ne maîtrisez pas conspirent pour lui donner l'air d'être sans effort. La carte réseau reçoit des données, elle déclenche une interruption pour informer la CPU. Le gestionnaire d'interruptions du système d'exploitation le gère. Ensuite, un système d'arrière-plan que vous ne contrôlez pas (créé en vous inscrivant à l'événement et dormant depuis que vous vous êtes enregistré pour l'événement) est réveillé par le système d'exploitation dans le cadre de la gestion de l'événement et exécute votre gestionnaire d'événements.

pierre précieuse
la source
7

Je vais aller à l'encontre de toutes les autres réponses que je vois jusqu'à présent et dire «oui» . Je pense que les autres réponses compliquent trop les choses. D'un point de vue conceptuel, toutes les boucles d'événements sont essentiellement:

while <the_program_is_running> {
    event=wait_for_next_event()
    process_event(event)
}

Si vous essayez de comprendre les boucles d'événement pour la première fois, les considérer comme une simple boucle ne vous fera pas de mal. Certaines infrastructures sous-jacentes attendent que le système d'exploitation livre un événement, il l'achemine ensuite vers un ou plusieurs gestionnaires, puis attend l'événement suivant, etc. C'est vraiment tout ce qu'il y a à faire du point de vue d'un logiciel d'application.

Bryan Oakley
la source
1
+1 pour être simple. Mais l'accent est mis sur "l'attente"; l'exécution de ce code s'ARRÊTERA sur la première ligne de la boucle et ne continuera pas tant qu'un événement ne se produira pas. Cela diffère d'une boucle d'interrogation occupée qui vérifie de manière répétée un événement jusqu'à ce qu'il se produise.
Tom Panning
1

Tous les déclencheurs d'événements ne sont pas gérés en boucle. Voici comment j'écris souvent mes propres moteurs d’événements:

interface Listener {
    void handle (EventInfo info);
}

List<Listener> registeredListeners

void triggerEvent (EventInfo info) {
    foreach (listener in registeredListeners) { // Memo 1
        listener.handle(info) // the handling may or may not be synchronous... your choice
    }
}

void somethingThatTriggersAnEvent () {
    blah
    blah
    blah
    triggerEvent(someGeneratedEventInfo)
    more blah
}

Notez que, bien que Memo 1 soit en boucle, la boucle sert à notifier chaque écouteur. Le déclencheur d'événement lui-même n'est pas nécessairement dans une boucle.

En théorie, les événements clés au niveau du système d'exploitation peuvent utiliser la même technique (bien que je pense qu'ils font souvent des interrogations à la place? Je spécule ici), à condition que le système d'exploitation expose une sorte d' registerListenerAPI.

Thomas Eding
la source