código arrumado para IO assíncrono

votos
20

Enquanto IO assíncrono (descritores non-blocking com select / votação / epoll / kqueue etc) não é a coisa mais documentado na web, há um punhado de bons exemplos.

No entanto, todos estes exemplos, tendo determinado as alças que são devolvidos pela chamada, basta ter um ' do_some_io(fd)' topo. Eles realmente não explicar a melhor forma de abordar o IO assíncrono real de tal método.

Bloqueio IO é muito arrumado e fácil de ler o código. Não-bloqueio, IO assíncrono é, por outro lado, peluda e confuso.

Que abordagens existem? O que são robustas e de fácil leitura?

void do_some_io(int fd) {
  switch(state) {
    case STEP1:
       ... async calls
       if(io_would_block)
          return;
       state = STEP2;
    case STEP2:
       ... more async calls
       if(io_would_block)
          return;
       state = STEP3;
    case STEP3:
       ...
  }
}

ou talvez (ab) usando gotos computadas do GCC:

#define concatentate(x,y) x##y
#define async_read_xx(var,bytes,line)       \
   concatentate(jmp,line):                  \
   if(!do_async_read(bytes,&var)) {         \
       schedule(EPOLLIN);                   \
       jmp_read = &&concatentate(jmp,line); \
       return;                              \
}

// macros for making async code read like sync code
#define async_read(var,bytes) \
    async_read_xx(var,bytes,__LINE__)

#define async_resume()            \
     if(jmp_read) {               \
         void* target = jmp_read; \
         jmp_read = NULL;         \
         goto *target;            \
     }

void do_some_io() {
   async_resume();
   async_read(something,sizeof(something));
   async_read(something_else,sizeof(something_else));
}

Ou talvez exceções C ++ e uma máquina de estado, de modo que as funções de trabalho pode desencadear a pouco abort / currículo, ou talvez um estado-máquina baseada em tabelas?

A sua não é como fazê-lo funcionar, a sua forma de torná-lo sustentável que eu estou perseguindo!

Publicado 19/05/2009 em 15:33
fonte usuário
Em outras línguas...                            


5 respostas

votos
16

Eu sugiro dar uma olhada em: http://www.kegel.com/c10k.html , segundo uma olhada em bibliotecas existentes como libevent, Boost.Asio que já fazem o trabalho e ver como eles funcionam.

O ponto é que a abordagem pode ser diferente para cada tipo de chamada de sistema:

  • select é reactor simples
  • epoll ter tanto borda ou interface desencadeada nível que exigem abordagem diferente
  • iocp é proactor exigem outra abordagem

Sugestão: use o bom biblioteca existente como Boost.Asio para C ++ ou libevent para C.

EDIT: Isto é como ASIO lida com isso

class connection {
   boost::asio:ip::tcp::socket socket_;
public:
   void run()
   {
         // for variable length chunks
         async_read_until(socket_,resizable_buffer,'\n',
               boost::bind(&run::on_line_recieved,this,errorplacehplder);
         // or constant length chunks
         async_read(socket_,buffer(some_buf,buf_size),
               boost::bind(&run::on_line_recieved,this,errorplacehplder);
   }
   void on_line_recieved(error e)
   {
        // handle it
        run();
   }

};

Porque ASIO funciona como proactor ele avisa quando a operação está completa e manipula EWOULDBLOCK internamente.

Se você palavra como reator você pode simular este comportamento:

 class conn {
    // Application logic

    void run() {
       read_chunk(&conn::on_chunk_read,size);
    }
    void on_chunk_read() {
         /* do something;*/
    }

    // Proactor wrappers

    void read_chunk(void (conn::*callback),int size, int start_point=0) {
       read(socket,buffer+start,size)
       if( complete )
          (this->*callback()
       else {
          this -> tmp_size-=size-read;
          this -> tmp_start=start+read;
          this -> tmp_callback=callback
          your_event_library_register_op_on_readable(callback,socket,this);
       }
    }
    void callback()
    {
       read_chunk(tmp_callback,tmp_size,tmp_start);
    }
 }

Algo parecido.

Respondeu 19/05/2009 em 16:13
fonte usuário

votos
5

máquinas de estado são uma boa abordagem. É um pouco de complexidade na frente que vai lhe poupar dores de cabeça no futuro, onde o futuro começa muito, muito em breve. ;-)

Um outro método é a utilização de fios e fazer o bloqueio de E / S num único fd em cada segmento. O trade-off aqui é que você faz E / S simples, mas pode introduzir uma complexidade na sincronização.

Respondeu 19/05/2009 em 15:51
fonte usuário

votos
3

Grande design padrão "coroutine" existe para resolver este problema.

É o melhor dos dois mundos: código arrumado, exatamente como fluxo io síncrona e grande desempenho sem troca de contexto, como assíncrono io dá. Coroutine olha para dentro como um fio síncrona odinary, com ponteiro de instrução única. Mas muitos coroutines pode ser executado dentro de um segmento OS (assim chamada "multitarefa cooperativa").

Exemplo código co-rotina:

void do_some_io() {
   blocking_read(something,sizeof(something));
   blocking_read(something_else,sizeof(something_else));
   blocking_write(something,sizeof(something));
}

Parece que o código síncrono, mas no fluxo de controle fato de usar uma outra maneira, como este:

void do_some_io() {
   // return control to network io scheduler, to handle another coroutine
   blocking_read(something,sizeof(something)); 
   // when "something" is read, scheduler fill given buffer and resume this coroutine 

   // return control to network io scheduler, to handle another coroutine
   CoroSleep( 1000 );
   // scheduler create async timer and when it fires, scheduler pass control to this coroutine
    ...
   // and so on 

Assim único segmento de controle de programador muitas coroutines com código definido pelo usuário e chamadas síncrona semelhantes arrumado para io.

Exemplo C ++ implementação coroutines é "boost.coroutine" (na verdade, não uma parte do impulso :) http://www.crystalclearsoftware.com/soc/coroutine/ Esta biblioteca implementa totalmente mecânica coroutine e pode usar boost.asio como programador e assíncrona io camada.

Respondeu 30/03/2012 em 21:05
fonte usuário

votos
1

Você precisa ter um loop principal que fornece async_schedule (), async_foreach (), async_tick (etc.) Estas funções em entradas do lugar se transformar em uma lista global de métodos que serão executados após a próxima chamada para async_tick (). Depois, você pode escrever código que é muito mais arrumado e não inclui quaisquer declarações switch.

Você pode apenas escrever:

async_schedule(callback, arg, timeout); 

Ou:

async_wait(condition, callback, arg, timeout); 

Em seguida, sua condição pode até mesmo ser definido em outro segmento (desde que você cuidar da segurança do thread ao acessar essa variável).

Eu tenho implementado um quadro assíncrona em C para o meu projeto incorporado porque eu queria ter multitarefa não-preferência e assíncrona é perfeito para fazer muitas tarefas, fazendo um pouco de trabalho durante cada iteração do loop principal.

O código está aqui: https://github.com/mkschreder/fortmax-blocks/blob/master/common/kernel/async.c

Respondeu 04/10/2014 em 19:01
fonte usuário

votos
0

Você quer dissociar "io" de processamento, altura em que o código que você leia vai se tornar muito legível. Basicamente, você tem:


    int read_io_event(...) { /* triggers when we get a read event from epoll/poll/whatever */

     /* read data from "fd" into a vstr/buffer/whatever */

     if (/* read failed */) /* return failure code to event callback */ ;

     if (/* "message" received */) return process_io_event();

     if (/* we've read "too much" */) /* return failure code to event callback */ ;

     return /* keep going code for event callback */ ;
    }


    int process_io_event(...) {
       /* this is where you process the HTTP request/whatever */
    }

... então o código real é em caso de processo, e mesmo se você tiver várias solicitações respostas é bastante legível, você acabou de fazer "read_io_event return ()" depois de definir um estado ou o que quer.

Respondeu 19/05/2009 em 19:35
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more