Um loop de eventos é apenas um loop for / while com pesquisa otimizada?

55

Estou tentando entender o que é um loop de eventos. Geralmente, a explicação é que, em um loop de eventos, você faz algo até ser notificado de que um evento ocorreu. Você lida com o evento e continua fazendo o que estava fazendo antes.

Para mapear a definição acima com um exemplo. Eu tenho um servidor que 'escuta' em um loop de eventos e, quando uma conexão de soquete é detectada, os dados são lidos e exibidos, após o que o servidor continua / começa a escutar como antes.


No entanto, esse evento acontecendo e nós sermos notificados 'exatamente assim' são muito para eu lidar. Você pode dizer: "Não é 'exatamente assim' que você precisa registrar um ouvinte de evento". Mas o que é um ouvinte de eventos, mas uma função que, por algum motivo, não está retornando. Está em seu próprio loop, aguardando para ser notificado quando um evento acontece? O ouvinte de evento também deve registrar um ouvinte de evento? Onde isso termina?


Eventos são uma boa abstração para se trabalhar, mas apenas uma abstração. Acredito que, no final, as pesquisas são inevitáveis. Talvez não estejamos fazendo isso em nosso código, mas os níveis mais baixos (a implementação da linguagem de programação ou o SO) estão fazendo isso por nós.

Basicamente, resume-se ao pseudo-código a seguir, que está sendo executado em algum lugar baixo o suficiente para não resultar em espera ocupada:

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

Esta é a minha compreensão de toda a ideia e gostaria de saber se isso está correto. Estou aberto a aceitar que toda a idéia está fundamentalmente errada, caso em que eu gostaria da explicação correta.

TheMeaningfulEngineer
fonte
3
Sistemas de eventos são implementações do padrão Observer . A compreensão do padrão deve consolidar sua compreensão dos eventos. Nenhuma pesquisa é necessária.
Steven Evers
11
Em última análise, sim, mesmo que a linguagem construa abstraí-la.
GrandmasterB
@SteveEvers No seu link wiki. O que está EventSourcefazendo se não estiver pesquisando a entrada do teclado?
TheMeaningfulEngineer
@ Alan: Poderia estar fazendo qualquer coisa, mas para a entrada do teclado especificamente, existem APIs para registrar seu aplicativo como ouvindo eventos do teclado, que você pode usar como fonte de eventos sem nenhuma pesquisa envolvida. Obviamente, se você descer o suficiente, o USB estará sempre pesquisando, mas vamos supor que estamos usando um teclado PS / 2, que é acionado por interrupção, e então você tem uma pilha de entrada de teclado com base em eventos com pesquisa zero.
Phoshi
Eu tenho essas perguntas há anos, mas não sei por que nunca me preocupei em fazer isso. Obrigado, agora estou satisfeito com o entendimento que @Karl Bielefeldt me esclareceu.
0xc0de

Respostas:

54

A maioria dos loops de eventos será suspensa se não houver eventos prontos, o que significa que o sistema operacional não dará à tarefa nenhum tempo de execução até que um evento aconteça.

Digamos que o evento seja uma tecla pressionada. Você pode perguntar se há um loop em algum lugar do sistema operacional, procurando por pressionamentos de tecla. A resposta é não. As teclas pressionadas geram uma interrupção , que é tratada de forma assíncrona pelo hardware. Da mesma forma para temporizadores, movimentos do mouse, chegada de um pacote etc.

De fato, para a maioria dos sistemas operacionais, a pesquisa de eventos é a abstração. O hardware e o sistema operacional lidam com eventos de forma assíncrona e os colocam em uma fila que pode ser pesquisada por aplicativos. Você realmente vê a sondagem verdadeira no nível do hardware nos sistemas incorporados e nem sempre existe.

Karl Bielefeldt
fonte
4
Uma interrupção não é uma mudança de tensão em um fio? Isso pode desencadear um evento por si só ou devemos pesquisar o pino quanto ao valor da tensão?
TheMeaningfulEngineer
8
Isso pode desencadear um evento por si só. O processador foi projetado dessa maneira. De fato, um processador pode ser despertado do modo de suspensão por uma interrupção.
Karl Bielefeldt
2
Estou confuso com a afirmação Most event loops will block. Como isso se encaixa no "paradigma do loop de eventos, em oposição ao uso de threads, usa chamadas assíncronas sem bloqueio"?
TheMeaningfulEngineer
11
Se você olhar para os loops de eventos de uma biblioteca como o GTK +, eles procurarão novos eventos e chamarão os manipuladores de eventos em um loop, mas se não houver nenhum evento, eles bloquearão um semáforo ou um cronômetro ou algo assim. Os desenvolvedores individuais criam seus próprios loops de eventos que não são bloqueados em uma fila de eventos vazia, mas as bibliotecas amplamente usadas são bloqueadas. Caso contrário, os loops de eventos são ineficientes.
Karl Bielefeldt
11
Suponho que você possa ver dessa maneira, mas é multiencadeamento na biblioteca e / ou no SO, e não no código do aplicativo. Os loops de evento abstraem os problemas de sincronização para o desenvolvedor do aplicativo.
Karl Bielefeldt
13

Penso em um ouvinte de evento não como uma função executando seu próprio loop, mas como uma corrida de revezamento com o primeiro corredor esperando a arma de partida. Uma razão significativa para usar eventos em vez de pesquisar é que eles são mais eficientes nos ciclos da CPU. Por quê? Olhe para ele a partir do hardware (em vez do código fonte para baixo).

Considere um servidor da Web. Quando o servidor chama listen()e bloqueia, seu código está assumindo seu lugar como corredor de retransmissão. Quando o primeiro pacote de uma nova conexão chega, a placa de rede inicia a corrida interrompendo o sistema operacional. O sistema operacional executa uma rotina de serviço de interrupção (ISR) que agarra o pacote. O ISR passa o bastão para uma rotina de nível superior que estabelece a conexão. Quando a conexão está ativa, essa rotina passa o bastão para listen(), que passa o bastão para o seu código. Nesse ponto, você pode fazer o que quiser com a conexão. Pelo que sabemos, entre as corridas, cada corredor de revezamento poderia ir ao pub. Uma força da abstração do evento é que seu código não precisa saber ou se importar.

Alguns sistemas operacionais incluem código de manipulação de eventos que executa sua parte da corrida, retira o bastão e volta ao ponto inicial para aguardar o início da próxima corrida. Nesse sentido, a manipulação de eventos é otimizada em vários loops simultâneos. No entanto, sempre há um gatilho externo que inicia o processo. O ouvinte de eventos não é uma função que não está retornando, mas uma função que aguarda esse gatilho externo antes de ser executado. Ao invés de:

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

Eu penso nisso como:

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

e entre a signale a próxima vez que o manipulador for executado, conceitualmente não há código em execução ou em loop.

cxw
fonte
Isso faz sentido, no entanto, o que o loop de eventos está fazendo enquanto aguarda a interrupção? Se está bloqueando, não é essa a abordagem 'multiencadeada', que é o paradigma oposto ao loop de eventos?
TheMeaningfulEngineer
Se o seu código tiver o loop forever: { select(); do stuff; }, você estará entrando novamente na corrida todas as vezes através do loop. Se você faz isso repetidamente a partir de um único thread ou em paralelo em threads ou processadores separados, penso em cada evento como sua própria raça. Por exemplo, um navegador da Web é um programa multithread com vários loops de eventos em threads separados, pelo menos um para a interface do usuário e um para cada página que está baixando. A pergunta que faço ao codificar é "como posso processar eventos com rapidez suficiente?" Às vezes a resposta é um loop, às vezes threads, geralmente uma combinação.
Cxw # 18/13
Faço programação baseada em eventos há mais de duas décadas e achei essa resposta muito confusa. Para que um ouvinte funcione, deve haver um loop que aguarde um evento e o direcione para todos os ouvintes registrados. (Supondo que estamos falando de software, em vez de interrupções de hardware)
Bryan Oakley
8

Não. Não é "pesquisa otimizada". Um loop de eventos usa E / S orientada a interrupção em vez de pesquisa.

Enquanto, Até, Para etc., os loops são loops de polling.

"Polling" é o processo de verificar repetidamente algo. Como o código do loop é executado continuamente e por ser um loop pequeno e "apertado", há pouco tempo para o processador alternar tarefas e fazer qualquer outra coisa. Quase todos os "travamentos", "congelamentos", "travamentos" ou o que você quiser chamar quando o computador não responde, são a manifestação do código preso em um loop de pesquisa não intencional. A instrumentação mostrará 100% de uso da CPU.

Os loops de eventos controlados por interrupções são muito mais eficientes do que os loops de polling. A pesquisa é um uso extremamente inútil dos ciclos da CPU, portanto, são feitos todos os esforços para eliminá-lo ou minimizá-lo.

No entanto, para otimizar a qualidade do código, a maioria dos idiomas tenta usar o paradigma do loop de polling o mais próximo possível dos comandos de entrega de eventos, pois eles servem a propósitos funcionalmente semelhantes em um programa. Assim, com a pesquisa sendo a maneira mais familiar de esperar por um pressionamento de tecla ou algo assim, é fácil para os inexperientes usá-la e acabar com um programa que pode funcionar bem por si só, mas nada funciona enquanto está sendo executado. Ele "assumiu" a máquina.

Conforme explicado em outras respostas, na entrega de eventos acionada por interrupção, essencialmente um "sinalizador" é definido na CPU e o processo é "suspenso" (não é permitido executar) até que esse sinalizador seja alterado por algum outro processo (como o teclado driver mudando quando o usuário pressiona uma tecla). Se o sinalizador for uma condição real de hardware, como uma linha sendo "puxada para o alto", será chamada de "interrupção" ou "interrupção de hardware". No entanto, a maioria é implementada como apenas um endereço de memória na CPU ou na memória principal (RAM) e é chamada de "semáforos".

Os semáforos podem ser alterados sob controle de software e, portanto, podem fornecer um mecanismo de sinalização muito rápido e simples entre os processos de software.

Interrupções, no entanto, só podem ser alteradas por hardware. O uso mais onipresente de interrupções é o acionado em intervalos regulares pelo chip de relógio interno. Um dos inúmeros tipos de ações de software ativadas por interrupções do relógio é a alteração de semáforos.

Eu deixei muito de fora, mas tive que parar em algum lugar. Por favor, pergunte se você precisar de mais detalhes.

DocSalvager
fonte
7

Normalmente, a resposta é hardware, o sistema operacional e os threads de plano de fundo que você não controla conspiram para torná-lo fácil. A placa de rede recebe alguns dados e gera uma interrupção para informar a CPU. O manipulador de interrupções do sistema operacional lida com isso. Em seguida, um encadeamento em segundo plano que você não controla (que foi criado ao se registrar no evento e está em suspensão desde que você se registrou no evento) é acordado pelo sistema operacional como parte do tratamento do evento e executa seu manipulador de eventos.

pedregoso
fonte
7

Vou contra todas as outras respostas que vejo até agora e digo "sim" . Eu acho que as outras respostas estão complicando demais as coisas. Do ponto de vista conceitual, todos os loops de eventos são essencialmente:

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

Se você estiver tentando entender os loops de eventos pela primeira vez, pensar neles como um loop simples não fará mal. Alguma estrutura subjacente está aguardando o sistema operacional entregar um evento, depois o encaminha para um ou mais manipuladores, aguarda o próximo evento e assim por diante. Isso é realmente tudo o que existe, da perspectiva do software aplicativo.

Bryan Oakley
fonte
11
+1 por ser simples. Mas a ênfase está na "espera"; a execução desse código será interrompida na primeira linha do loop e não continuará até que um evento ocorra. Isso é diferente de um loop de pesquisa ocupado que verifica repetidamente um evento até que um ocorra.
Tom Panning
1

Nem todos os gatilhos de eventos são manipulados em loops. A maneira como costumo escrever meus próprios mecanismos de eventos seria assim:

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
}

Observe que, embora o Memo 1 esteja em um loop, o loop é para notificar cada ouvinte. O gatilho de evento em si não está necessariamente em um loop.

Em teoria, os principais eventos no nível do sistema operacional podem usar a mesma técnica (embora eu ache que eles costumam fazer pesquisas? Estou apenas especulando aqui), desde que o sistema operacional exponha algum tipo de registerListenerAPI.

Thomas Eding
fonte