Se necesita cierta aclaración sobre las operaciones asio síncronas y asíncronas

Por lo que yo sé, la principal diferencia entre las operaciones sincrónicas y asíncronas. Es decir, write() o read() frente a async_write() y async_read() es que los primeros, no regresan hasta que la operación termine -o error-, y los últimos, vuelven inmediatamente.

Debido al hecho de que las operaciones asincrónicas están controladas por un io_service.run() que no termina hasta que las operaciones controladas hayan finalizado. Me parece que en operaciones secuenciales como aquellas involucradas en conexiones TCP / IP con protocolos como POP3, en las cuales la operación es una secuencia como:

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

La diferencia entre operadores sincrónicos / asincrónicos no tiene mucho sentido.

Por supuesto, en ambas operaciones siempre existe el riesgo de que el flujo del progtwig se detenga indefinidamente por alguna circunstancia -algo el uso de temporizadores-, pero me gustaría conocer algunas opiniones más autorizadas en este asunto.

Debo admitir que la pregunta está bastante mal definida, pero me gustaría escuchar algunos consejos sobre cuándo usar uno u otro. Me he encontrado con problemas al depurar con MS Visual Studio con respecto a las operaciones de SSL asíncronas en un cliente POP3 en el que ahora estoy trabajando, y algunas veces pienso que quizás sea una mala idea usar asincrono en esto.

La documentación de Boost.Asio realmente hace un trabajo fantástico al explicar los dos conceptos. Como mencionó Ralf, Chris también tiene un gran blog que describe conceptos asincrónicos. El ejemplo del parquímetro que explica cómo funcionan los tiempos de espera es particularmente interesante, como lo es el ejemplo ilustrado .

Primero, considere una operación de conexión síncrona:

conexión sincrónica

El flujo de control es bastante sencillo aquí, su progtwig invoca una API (1) para conectar un socket. La API utiliza un servicio de E / S (2) para realizar la operación en el sistema operativo (3). Una vez que se completa esta operación (4 y 5), el control regresa a su progtwig inmediatamente después (6) con alguna indicación de éxito o falla.

La operación análoga análoga tiene un flujo de control completamente diferente:

conexión asíncrona

Aquí, su aplicación inicia la operación (1) utilizando el mismo servicio de E / S (2), pero el flujo de control se invierte. La finalización de la operación hace que el servicio de E / S notifique a su progtwig a través de un controlador de finalización. El tiempo entre el paso 3 y el momento en que se completó la operación se contuvo por completo dentro de la operación de conexión para el caso síncrono.

Puede ver que el caso sincrónico es naturalmente más fácil de entender para la mayoría de los progtwigdores porque representa los paradigmas de flujo de control tradicionales. El flujo de control invertido utilizado por las operaciones asincrónicas es difícil de entender, a menudo obliga a su progtwig a dividir las operaciones en el start y handle métodos donde se mueve la lógica. Sin embargo, una vez que tenga una comprensión básica de este flujo de control, se dará cuenta de lo poderoso que es realmente el concepto. Algunas de las ventajas de la progtwigción asincrónica son:

  • Desacoplamientos a partir de la concurrencia. Realice una operación de larga ejecución; para el caso síncrono, a menudo crearía un hilo separado para manejar la operación y evitar que la GUI de la aplicación deje de responder. Este concepto funciona bien a pequeña escala, pero rápidamente se desmorona en un puñado de hilos.

  • Mayor rendimiento. El diseño de hilo por conexión simplemente no escala. Vea el problema C10K .

  • Composición (o encadenamiento). Las operaciones de nivel superior pueden estar compuestas de múltiples manejadores de finalización. Considere la posibilidad de transferir una imagen JPEG, el protocolo podría dictar que los primeros 40 bytes incluyan un encabezado que describa el tamaño de la imagen, la forma, tal vez alguna otra información. El primer controlador de finalización para enviar este encabezado puede iniciar la segunda operación para enviar los datos de imagen. La operación de nivel superior sendImage() no necesita saber o preocuparse acerca del método de encadenamiento utilizado para implementar la transferencia de datos.

  • Tiempos de espera y capacidad de cancelación. Hay formas específicas de plataforma para SO_RCVTIMEO tiempo de espera de una operación de larga ejecución (por ejemplo, SO_RCVTIMEO y SO_SNDTIMEO ). El uso de operaciones asincrónicas permite el uso de deadline_timer cancelando las operaciones de larga ejecución en todas las plataformas compatibles.


Por supuesto, en ambas operaciones siempre existe el riesgo de que el flujo del progtwig se detenga indefinidamente por alguna circunstancia -el uso de temporizadores-, pero me gustaría conocer algunas opiniones más autorizadas en este asunto.

Mi experiencia personal con Asio proviene del aspecto de escalabilidad. El software de escritura para superordenadores requiere una cantidad considerable de cuidado cuando se trata de recursos limitados como memoria, hilos, tomas de stream, etc. Usar un hilo por conexión para ~ 2 millones de operaciones simultáneas es un diseño que está muerto al llegar.

Supongo que la elección de sincrónico / asincrónico es muy específico de la aplicación. Estoy de acuerdo en que el paradigma asíncrono puede hacer que el código y la depuración sean mucho más complejos, pero tiene sus ventajas.

Para ilustrar, la razón principal por la que cambiamos de IO sincrónico para impulsar asio usando async IO es que en nuestra aplicación, bloquear IO no era una opción, tenemos un servidor de transmisión multimedia en el que estaba transmitiendo paquetes de medios a varios clientes después de haber sido codificado. El problema era que los problemas de red daban como resultado que toda la canalización de captura-encoding-entrega se bloquease efectivamente (por ejemplo, si la conexión con un único cliente fallaba).

Para resumir, en mi (ltd) experiencia con IO asíncrono, puede ser útil en situaciones en las que tiene que realizar otro trabajo mientras espera que se complete el IO (como servir a otros clientes, etc.). En sistemas o escenarios, donde tiene que esperar que el resultado del IO continúe, sería mucho más simple usar IO sincrónico.

También tendría sentido en los sistemas de comunicación dúplex (por ejemplo, protocolos más complejos como SIP, RTSP donde tanto el cliente como el servidor pueden enviar solicitudes). Ha pasado un tiempo desde que he tratado con POP, pero para el intercambio simple en su ejemplo, async IO podría considerarse excesivo. Cambiaría a Async IO solo una vez que estaba seguro de que sincronizar IO no es suficiente para cumplir con mis requisitos.

WRT para impulsar la documentación de Asio, descubrí que la mejor manera de entenderlo era trabajar con los ejemplos. Además, es posible que desee consultar un enlace en http://en.highscore.de/cpp/boost/index.html. Tiene un capítulo muy bueno sobre el aumento de asio. También el blog de Chris Kohlhoff (autor de asio) tiene algunos artículos realmente excelentes que vale la pena consultar.

Una respuesta simple es: (cita de aquí con poca modificación)

En los clientes Sync, todo sucede en serie. Si envía un comando, se pondrá en cola detrás del anterior hasta que termine. Por lo general, una aplicación de subproceso único.

En ASync todo sucede simultáneamente. Por lo general, aplicaciones de múltiples subprocesos que llevarán a cabo múltiples tareas al mismo tiempo.

sincrónico es fácil de controlar el flujo del progtwig.

asincrónico tiene un mejor rendimiento ya que no necesita guardar / restaurar registros para tareas de fibra.

asincrónico utiliza callback y difícil de progtwigr. Podemos probar promise-cpp para hacer que el flujo asíncrono sea sincrónico ,

Ejemplo de cliente 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); }); 

Código completo aquí