web-dev-qa-db-fra.com

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

J'essaie de comprendre ce qu'est une boucle d'événement. L'explication est souvent 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.

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


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


Les événements sont une belle abstraction avec laquelle travailler, mais juste 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 se résume essentiellement au pseudo-code suivant qui s'exécute quelque part suffisamment bas pour ne pas entraîner une attente occupée:

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

C'est ma compréhension de l'idée dans son ensemble, et j'aimerais savoir si c'est correct. Je suis ouvert à accepter que l'idée dans son ensemble est fondamentalement erronée, auquel cas j'aimerais avoir la bonne explication.

57

La plupart des boucles d'événements seront suspendues s'il n'y a aucun événement prêt, ce qui signifie que le système d'exploitation ne donnera à la tâche aucun temps d'exécution jusqu'à ce qu'un événement se produise.

Dites que l'événement est une touche enfoncée. Vous pourriez demander s'il y a une boucle quelque part dans le système d'exploitation vérifiant les pressions de touches. La réponse est non. Les touches enfoncées génèrent un interruption , qui est géré 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 des événements est l'abstraction. Le matériel et le système d'exploitation gèrent les événements de manière asynchrone et les placent dans une file d'attente pouvant être interrogée par les applications. Vous ne voyez vraiment que la véritable interrogation au niveau matériel dans les systèmes embarqués, et même là pas toujours.

56
Karl Bielefeldt

Je pense à un auditeur d'événements non pas comme une fonction exécutant sa propre boucle, mais comme une course de relais avec le premier coureur en attente du pistolet de départ. Une raison importante pour utiliser des événements au lieu d'interroger est qu'ils sont plus efficaces avec les cycles CPU. Pourquoi? Regardez-le à partir du matériel (plutôt que du code source).

Prenons un serveur Web. Lorsque votre serveur appelle listen() et bloque, votre code prend sa place de coureur de relais. Lorsque le premier paquet d'une nouvelle connexion arrive, la carte réseau démarre la course en interrompant le système d'exploitation. Le système d'exploitation exécute une routine de service d'interruption (ISR) qui saisit le paquet. L'ISR passe le relais à une routine de niveau supérieur qui établit la connexion. Une fois la connexion établie, cette routine passe le témoin à listen(), qui transmet le témoin à votre code. À ce stade, vous pouvez faire ce que vous voulez avec la connexion. Pour tout ce que nous savons, entre les courses, chaque coureur de relais pourrait se rendre au pub. Une force de l'abstraction de l'événement est que votre code n'a pas besoin de connaître ou de prendre soin.

Certains systèmes d'exploitation incluent un code de gestion des événements qui exécute sa partie de la course, passe le relais, puis revient à son point de départ pour attendre que la prochaine course commence. En ce sens, la gestion des événements est une interrogation optimisée dans de nombreuses boucles simultanées. Cependant, il y a toujours un déclencheur extérieur qui démarre 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 de s'exécuter. 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 signal et la prochaine fois que le gestionnaire s'exécute, il n'y a conceptuellement aucun code en cours d'exécution ou en boucle.

13
cxw

Non. Ce n'est pas un "sondage optimisé". ne boucle d'événements utilise des E/S déclenchées par interruption au lieu d'interroger.

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. Comme le code de boucle s'exécute en continu, et parce que c'est une petite boucle "serrée", le processeur a peu de temps pour changer de tâche et faire autre chose. Presque tous les "blocages", "gels", "blocages" ou tout ce que vous voulez l'appeler lorsque l'ordinateur ne répond plus, sont la manifestation du code coincé dans une boucle d'interrogation involontaire. L'instrumentation affichera 100% d'utilisation du processeur.

Les boucles d'événements déclenchées par interruption sont beaucoup plus efficaces que les boucles d'interrogation. L'interrogation est une utilisation extrêmement gaspilleuse des cycles CPU, donc tous les efforts sont faits pour l'éliminer ou le minimiser.

Cependant, pour optimiser la qualité du code, la plupart des langages essaient d'utiliser le paradigme de la boucle d'interrogation aussi étroitement que possible pour les commandes de gestion d'événements car elles servent des objectifs fonctionnellement similaires dans un programme. Ainsi, avec l'interrogation étant le moyen le plus familier d'attendre une pression sur une touche ou quelque chose, il est facile pour les inexpérimentés de l'utiliser et de se retrouver avec un programme qui peut fonctionner correctement par lui-même, mais rien d'autre ne fonctionne pendant son fonctionnement. Il a "repris" la machine.

Comme expliqué dans d'autres réponses, dans la gestion d'événements déclenchée par interruption, un "indicateur" est essentiellement défini dans le processeur 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 changement de pilote 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", elle est appelée "interruption" ou "interruption matérielle". Cependant, la plupart sont implémentés comme une simple adresse mémoire sur le processeur ou dans la mémoire principale (RAM) et sont appelés "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 omniprésente des interruptions est celle déclenchée à intervalles réguliers par la puce d'horloge interne. L'un des innombrables types d'actions logicielles activées par les interruptions d'horloge est le changement de sémaphores.

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

8
DocSalvager

En règle générale, la réponse est le matériel, le système d'exploitation et les threads d'arrière-plan que vous ne contrôlez pas conspirent pour le rendre sans effort. La carte réseau reçoit des données, elle déclenche un interruption pour informer le CPU. Le gestionnaire d'interruption du système d'exploitation le gère. Ensuite, un thread d'arrière-plan que vous ne contrôlez pas (créé en vous inscrivant à l'événement et dormant depuis que vous vous êtes inscrit à 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.

7
stonemetal

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énements pour la première fois, les considérer comme une simple boucle ne fera aucun mal. Un cadre sous-jacent attend que le système d'exploitation délivre un événement, il achemine ensuite l'événement vers un ou plusieurs gestionnaires, puis attend l'événement suivant, etc. C'est vraiment tout ce qu'il y a d'un point de vue logiciel d'application.

7
Bryan Oakley

Tous les déclencheurs d'événements ne sont pas gérés en boucles. La façon dont j'écris souvent mes propres moteurs d'événements serait la suivante:

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 sur une boucle, la boucle sert à notifier chaque auditeur. 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 effectuent souvent des interrogations à la place? Je ne fais que spéculer ici), à condition que le système d'exploitation expose une sorte d'API registerListener.

1
Thomas Eding