É um loop de evento apenas um loop for / while com pesquisa otimizada?

54

Estou tentando entender o que é um loop de eventos. Muitas vezes a explicação é que, em um loop de eventos, você faz alguma coisa até ser notificado de que um evento ocorreu. Você então 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 dela são lidos e exibidos, após o qual o servidor continua / começa a escutar como antes.

No entanto, esse evento está acontecendo e nós somos notificados 'assim mesmo' são muito para mim. Você pode dizer: "Não é 'apenas assim' você tem que registrar um ouvinte de evento". Mas o que é um ouvinte de evento, mas uma função que, por algum motivo, não está retornando. Está em seu próprio ciclo, esperando para ser notificado quando um evento acontecer? O ouvinte de evento também deve registrar um ouvinte de evento? Onde isso acaba?

Eventos são uma boa abstração para trabalhar, mas apenas uma abstração. Acredito que, no final, a votação seja inevitável. Talvez não o façamos em nosso código, mas os níveis mais baixos (a implementação da linguagem de programação ou o sistema operacional) estão fazendo isso por nós.

Basicamente se resume ao seguinte pseudocódigo que está rodando 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

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

    
por TheMeaningfulEngineer 18.10.2013 / 22:03
fonte

6 respostas

52

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

Diga que o evento é uma tecla sendo pressionada. Você pode perguntar se há um loop em algum lugar do sistema operacional que está verificando 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 timers, movimentos do mouse, um pacote chegando, etc.

Na verdade, para a maioria dos sistemas operacionais, a pesquisa de eventos é a abstração. O hardware e o SO manipulam eventos de forma assíncrona e os colocam em uma fila que pode ser consultada pelos aplicativos. Você só vê a pesquisa verdadeira no nível do hardware em sistemas embarcados, e nem sempre existe.

    
por 18.10.2013 / 22:42
fonte
13

Eu 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 pela arma inicial. Uma razão significativa para usar eventos em vez de pesquisa é que eles são mais eficientes com os ciclos da CPU. Por quê? Olhe para ele do hardware para cima (em vez do código-fonte para baixo).

Considere um servidor da Web. Quando seu servidor chama listen() e bloqueia, seu código está tomando seu lugar como um 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 estiver 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. Por tudo o que sabemos, entre corridas cada corredor de revezamento poderia ir ao pub. Uma força da abstração de eventos é que seu código não precisa saber ou se importar.

Alguns sistemas operacionais incluem o código de manipulação de eventos que executa sua parte da corrida, retira o bastão e, em seguida, retorna ao seu ponto de partida 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, há sempre um acionador externo que inicia o processo. O ouvinte de evento não é uma função que não está retornando, mas uma função que está aguardando esse acionador externo antes de ser executada. Em vez 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 o signal e a próxima vez que o manipulador é executado, não há conceitualmente código em execução ou looping.

    
por 18.10.2013 / 22:43
fonte
8

Não. Não é "pesquisa otimizada". Um loop de eventos usa E / S controlada por interrupções em vez de pesquisa.

Os loops While, Until, For, etc. são loops de polling.

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

Os loops de evento orientados por interrupções são muito mais eficientes que os loops de pesquisa. O polling é um uso extremamente dispendioso dos ciclos da CPU, portanto, todo esforço é feito para eliminá-lo ou minimizá-lo.

No entanto, para otimizar a qualidade do código, a maioria das linguagens tenta usar o paradigma do ciclo de pesquisa o mais próximo possível dos comandos de entrega de evento, uma vez que eles servem propósitos funcionalmente semelhantes dentro de um programa. Assim, com a sondagem 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 ser executado sozinho, mas nada funciona enquanto está em execução. Ele "assumiu" a máquina.

Como explicado em outras respostas, na manipulação de eventos acionada por interrupções, essencialmente um "sinalizador" é definido dentro da CPU e o processo é "suspenso" (não permitido a ser executado) até que o sinalizador seja alterado por algum outro processo como o driver do teclado mudando quando o usuário pressionou uma tecla). Se o sinalizador for uma condição de hardware real, como uma linha sendo "puxada para cima", ela é chamada de "interrupção" ou "interrupção de hardware". A maioria, no entanto, é implementada apenas como 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 frequente de interrupções é o acionado em intervalos regulares pelo chip do relógio interno. Um dos inúmeros tipos de ações de software ativadas por interrupções de clock é a mudança de semáforos.

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

    
por 25.10.2013 / 07:54
fonte
7

Normalmente, a resposta é o hardware, o SO e os tópicos de segundo plano que você não controla conspiram para parecer fácil. A placa de rede recebe alguns dados que gera uma interrupção para informar a CPU. O manipulador de interrupção do sistema operacional lida com ele. Então, um thread de segundo plano que você não controla (que foi criado ao se registrar para o evento e está dormindo desde que você se registrou no evento) é despertado pelo sistema operacional como parte do tratamento do evento e executa seu manipulador de eventos.

    
por 18.10.2013 / 22:43
fonte
7

Eu vou contra todas as outras respostas que vejo até agora e digo "yes" . Eu acho que as outras respostas estão complicando demais as coisas. De um ponto de vista conceitual, todos os loops de evento são essencialmente:

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

Se você está tentando entender os loops de eventos pela primeira vez, pensar neles como um simples loop não causará nenhum dano. Algumas estruturas subjacentes estão aguardando que o SO entregue um evento, ele encaminha o evento para um ou mais manipuladores, aguarda o próximo evento e assim por diante. Isso é realmente tudo o que existe a partir de uma perspectiva de software aplicativo.

    
por 19.10.2013 / 01:25
fonte
1

Nem todos os acionadores de eventos são tratados em loops. A maneira que eu frequentemente escrevo 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 acionador de evento em si não está necessariamente em um loop.

Em teoria, os eventos chave no nível do SO podem usar a mesma técnica (embora eu ache que eles geralmente façam polling? Estou apenas especulando aqui), desde que o SO exponha algum tipo de registerListener API.

    
por 18.10.2013 / 22:15
fonte