Sono necessari alcuni chiarimenti sulle operazioni asincrone contro asio asincrone

Per quanto ne so, la principale differenza tra le operazioni sincrone e asincrone. Ie write() o read() vs async_write() e async_read() è che il primo, non ritorna fino al termine dell’operazione -o errore-, e gli ultimi, restituisce immediatamente.

A causa del fatto che le operazioni asincrone sono controllate da un io_service.run() che non termina fino a quando le operazioni controllate non sono state finalizzate. Mi sembra che in operazioni sequenziali come quelle coinvolte nelle connessioni TCP / IP con protocolli come POP3, in cui l’operazione è una sequenza come:

  C:  S: Ok. C: User... S: Ok. C: Password S: Ok. C: Command S: answer C: Command S: answer ... C: bye S:  

La differenza tra operatori sincroni / asincroni non ha molto senso.

Ovviamente, in entrambe le operazioni c’è sempre il rischio che il stream del programma si fermi indefinitamente da qualche circostanza – a parte l’uso di timer -, ma vorrei sapere qualche opinione più autorizzata in questa materia.

Devo ammettere che la domanda è piuttosto mal definita, ma mi piacerebbe sentire qualche consiglio su quando usare l’una o l’altra. Ho riscontrato dei problemi durante il debugging con MS Visual Studio in merito alle operazioni asincrone SSL in un client POP3 su cui sto lavorando, ea volte penso che forse è una ctriggers idea usare asincrono in questo.

La documentazione di Boost.Asio fa davvero un ottimo lavoro spiegando i due concetti. Come ha detto Ralf, Chris ha anche un grande blog che descrive concetti asincroni. L’esempio del parchimetro che spiega come funzionano i timeout è particolarmente interessante, così come l’esempio illustrato di bind .

Innanzitutto, considera un’operazione di connessione sincrona:

connessione sincrona

Il stream di controllo è abbastanza semplice qui, il tuo programma invoca alcune API (1) per connettere un socket. L’API utilizza un servizio I / O (2) per eseguire l’operazione nel sistema operativo (3). Una volta completata questa operazione (4 e 5), il controllo ritorna immediatamente al tuo programma (6) con qualche indicazione di successo o fallimento.

L’analoga operazione asincrona ha un stream di controllo completamente diverso:

connessione asincrona

Qui, l’applicazione avvia l’operazione (1) utilizzando lo stesso servizio I / O (2), ma il stream di controllo è invertito. Il completamento dell’operazione fa sì che il servizio I / O notifichi il programma tramite un gestore di completamento. Il tempo tra il passaggio 3 e il completamento dell’operazione era contenuto interamente nell’operazione di connessione per il caso sincrono.

Puoi vedere che il caso sincrono è naturalmente più facile da comprendere per la maggior parte dei programmatori perché rappresenta i paradigmi del stream di controllo tradizionale. Il stream di controllo invertito utilizzato dalle operazioni asincrone è difficile da comprendere, spesso costringe il programma a suddividere le operazioni in start e handle metodi in cui la logica viene spostata. Tuttavia, una volta acquisita una conoscenza di base di questo stream di controllo, ti renderai conto di quanto sia potente il concetto. Alcuni dei vantaggi della programmazione asincrona sono:

  • Disaccoppia il threading dalla concorrenza. Operare in modo prolungato, per il caso sincrono si crea spesso un thread separato per gestire l’operazione per evitare che la GUI di un’applicazione non risponda. Questo concetto funziona bene su piccola scala, ma cade rapidamente in una manciata di fili.

  • Aumento delle prestazioni. Il design thread-per-connection semplicemente non scala. Vedi il problema C10K .

  • Composizione (o concatenamento). Le operazioni di livello superiore possono essere composte da più gestori di completamento. Considerare il trasferimento di un’immagine JPEG, il protocollo potrebbe dettare i primi 40 byte includere un’intestazione che descrive la dimensione dell’immagine, la forma, forse qualche altra informazione. Il primo gestore di completamento per inviare questa intestazione può avviare la seconda operazione per inviare i dati dell’immagine. L’operazione di livello superiore sendImage() non ha bisogno di sapere, o di preoccuparsi, del concatenamento del metodo utilizzato per implementare il trasferimento dei dati.

  • Timeout e annullamento-abilità. Esistono modalità specifiche della piattaforma per timeout di un’operazione di lunga durata (ad esempio: SO_RCVTIMEO e SO_SNDTIMEO ). L’utilizzo di operazioni asincrone consente l’utilizzo di deadline_timer annulla le operazioni di lunga durata su tutte le piattaforms supportate.


Ovviamente, in entrambe le operazioni c’è sempre il rischio che il stream del programma si fermi indefinitamente da qualche circostanza – a parte l’uso di timer -, ma vorrei sapere alcune opinioni più autorizzate in questa materia.

La mia esperienza personale con Asio deriva dall’aspetto della scalabilità. Scrivere software per supercomputer richiede una buona dose di attenzione quando si hanno a che fare con risorse limitate come memoria, thread, socket, ecc. Usare un thread-per-connection per ~ 2 milioni di operazioni simultanee è un progetto che è morto all’arrivo.

Suppongo che la scelta di sincrono / asincrono sia molto specifica per l’applicazione. Sono d’accordo che il paradigma asincrono può rendere il codice e il debug molto più complesso, ma ha i suoi benefici.

Per illustrare, il motivo principale per cui siamo passati da I / O sincrono a boost asio usando IO asincrono è che nel nostro blocco delle applicazioni IO non era un’opzione, abbiamo un server di streaming multimediale in cui stavo lo streaming di pacchetti multimediali su più client dopo essere stato codificato. Il problema era che i problemi di rete avevano portato all’interruzione effettiva della pipeline di acquisizione-codifica-distribuzione (ad es. Se la connessione a un singolo client non funzionava).

Riassumendo, nella mia (ltd) esperienza con IO asincrono, può essere utile in situazioni in cui si ha un altro lavoro che deve essere fatto mentre si attende il completamento dell’IO (come servire altri client, ecc.). Nei sistemi o negli scenari, in cui devi attendere che il risultato dell’IO continui, sarebbe molto più semplice utilizzare solo IO sincrono.

Avrebbe anche senso nei sistemi di comunicazione duplex (ad es. Protocolli più complessi come SIP, RTSP dove sia client che server possono inviare richieste). È passato un po ‘di tempo da quando mi sono occupato di POP, ma per il semplice scambio nel tuo esempio, l’IO asincrono potrebbe essere considerato eccessivo. Passerei ad IO asincrono solo una volta, quando ero sicuro che l’I / O di sincronizzazione non fosse sufficiente per soddisfare i miei requisiti.

WRT alla documentazione di boost asio, ho trovato che il modo migliore per ottenere il blocco di esso era di lavorare attraverso gli esempi. Inoltre, un link che potresti voler controllare è http://en.highscore.de/cpp/boost/index.html. C’è un capitolo davvero bello su boost asio. Anche il blog di Chris Kohlhoff (autore di asio) ha degli articoli davvero eccellenti che vale la pena di verificare.

Una risposta semplice è: (citazione da qui con poche modifiche)

Nei client di sincronizzazione tutto avviene in serie. Se invii un comando, verrà accodato dietro al precedente fino a quando non avranno finito. In genere un’applicazione a thread singolo.

In ASync tutto avviene simultaneamente. Solitamente applicazioni multi-thread che eseguiranno più attività contemporaneamente.

sincrono è facile da controllare il stream del programma.

asincrono ha prestazioni migliori dal momento che non è necessario salvare / ripristinare i registri per le attività in fibra.

asincrono usa callback e difficile da programmare. Possiamo provare promise-cpp per rendere il stream asincrono come sincrono

Esempio di client http –

 //<1> Resolve the host async_resolve(session->resolver_, host, port) .then([=](tcp::resolver::results_type &results) { //<2> Connect to the host return async_connect(session->socket_, results); }).then([=]() { //<3> Write the request return async_write(session->socket_, session->req_); }).then([=](std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); //<4> Read the response return async_read(session->socket_, session->buffer_, session->res_); }).then([=](std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); //<5> Write the message to standard out std::cout << session->res_ << std::endl; }).then([]() { //<6> success, return default error_code return boost::system::error_code(); }, [](const boost::system::error_code err) { //<6> failed, return the error_code return err; }).then([=](boost::system::error_code &err) { //<7> Gracefully close the socket std::cout << "shutdown..." << std::endl; session->socket_.shutdown(tcp::socket::shutdown_both, err); }); 

Codice completo qui