2011-01-19 18 views
9

Esto está relacionado de alguna manera con this question, pero creo que necesito saber un poco más. He estado tratando de entender cómo hacerlo durante unos días (mientras trabajo en otras partes), pero ha llegado el momento para que muerda la bala y se multiplique. Además, estoy buscando un poco más de información que la pregunta vinculada.boost :: asio, hilos y sincronización

En primer lugar, sobre multi-threading. Como he estado probando mi código, no me he molestado con ningún multi-threading. Es solo una aplicación de consola que inicia una conexión a un servidor de prueba y luego se maneja todo lo demás. El bucle principal es la siguiente:

while(true) 
{ 
    Root::instance().performIO(); // calls io_service::runOne(); 
} 

Cuando escribo mi solicitud principal, supongo que esta solución no será aceptable (ya que tendría que ser llamado en el bucle de mensajes que, aunque posible, tendría problemas cuando la cola de mensajes bloquea la espera de un mensaje. Puede cambiarlo para que el bucle de mensaje no se bloquee, pero ¿no va a golpear el uso de la CPU por el techo?)

La solución parece es arrojarle otro hilo. Bien vale. Pero luego he leído que io_service::run() regresa cuando no hay trabajo por hacer. ¿Que es eso? ¿Es eso cuando no hay datos, o no hay conexiones? Si existe al menos una conexión, ¿permanece viva? Si es así, no es un problema, ya que solo tengo que iniciar un nuevo hilo cuando se realiza la primera conexión y estoy contento si todo se detiene cuando no hay nada en absoluto. Supongo que estoy confundido por la definición de "no hay trabajo que hacer".

Luego tengo que preocuparme por la sincronización de mi hilo de impulso con mi hilo principal de GUI. Entonces, supongo que mis preguntas son:

  1. ¿Cuál es la mejor manera de usar boost :: asio en una aplicación cliente con respecto a los hilos y mantenerlos vivos?
  2. Al escribir en un socket desde el hilo principal al hilo IO, ¿se logra la sincronización utilizando boost::asio::post, de modo que la llamada se realice más tarde en el io_service?
  3. Cuando se reciben los datos, ¿cómo devuelve la gente la información al hilo de la interfaz de usuario? En el pasado, cuando usaba puertos de finalización, hice un evento especial que podría volver a publicar los datos en el hilo principal de UI usando un :: SendMessage. No fue elegante, pero funcionó.

Hoy leeré algo más, pero sería genial obtener un aviso de alguien que ya ha hecho esto. La documentación de Boost :: asio no es excelente, y la mayor parte de mi trabajo hasta ahora se ha basado en un poco de la documentación, algunas pruebas/errores, algún código de ejemplo en la web.

Respuesta

6

1) Eche un vistazo a io_service::work. Mientras exista un objeto de trabajo io_service :: run no regresará. Por lo tanto, si comienza a limpiar, destruya el objeto de trabajo, cancele las operaciones pendientes, por ejemplo, un async_read en un socket, espere a que la ejecución regrese y limpie sus recursos.

2) io_service :: post ejecutará asincrónicamente el controlador dado a partir de un hilo que ejecuta el io_service. Se puede usar una devolución de llamada para obtener el resultado de la operación ejecutada.

3) Necesita algún tipo de sistema de mensajería para informar a su hilo GUI de los nuevos datos. Hay varias posibilidades aquí.

En cuanto a su comentario sobre la documentación, creo que Asio es una de las bibliotecas de impulso mejor documentadas y viene con ejemplos claros.

+0

+1 io_service :: work es cómo yo mismo resuelvo este problema. –

+0

gracias por su respuesta. Cuando dice que hay varias posibilidades, ¿cuáles son las que ha usado en el pasado? –

+0

@ Moo-Juice, no he estado en una situación en la que haya un hilo de GUI específico que necesite manejar los datos. Acabo de utilizar el mecanismo de devolución de llamada y manejar los datos de un hilo io_service a medida que entró.Alguna forma de cola de mensajes que los subprocesos GUI leen, podría usarse para indicar la llegada de nuevos datos. O simplemente podría usar una variable condicional. Realmente depende de tu aplicación, diseño y requisitos. –

1

boost::io_service::run() volverán solo cuando no haya nada que hacer, por lo que no hay operaciones de sincronización pendientes, p. Async accept/connection, async read/write o async timer wait. entonces, antes de llamar al io_service::run() primero debe comenzar cualquier operación asíncrona.

no tengo ¿tienes consola o aplicación GUI? en cualquier caso, el multihilo parece una exageración. puede usar Asio junto con su ciclo de mensajes. si se trata de la GUI de Win32 puede llamar a io_service :: run_one() desde su controlador OnIdle(). en el caso de la aplicación de la consola, puede configurar deadline_timer que comprueba regularmente (cada 200 ms?) para la entrada del usuario y utilizarlo con io_service::run(). todo en un solo hilo para simplificar en gran medida la solución

2

1) ¿Cuál es la mejor manera-práctica de utilizar impulso :: asio en una aplicación cliente con respecto a las discusiones y manteniendo vivos?

A medida que el documentation suggests, un grupo de subprocesos que invocan io_service::run es la más escalable y fácil de implementar.

2) Al escribir a una toma de la principal hilo para el proceso de IO, se sincronización logrado usando impulso :: :: asio de entrada, por lo que la llamada ocurre más tarde en el io_service?

Deberá usar a strand para proteger cualquier controlador que pueda ser invocado por varios hilos. Consulte this answer ya que puede ser útil, al igual que en este ejemplo.

3) Cuando se reciben datos, ¿cómo la gente devuelve los datos al hilo de la interfaz de usuario? En el pasado cuando utilicé los puertos de terminación, Hice un evento especial que podría publicar los datos de nuevo en la hebra UI principal usando un :: SendMessage. No fue elegante, pero funcionó.

¿Qué hay de proporcionar una devolución de llamada en la forma de un boost::function cuando se registra un evento asíncrono a la io_service? Luego, el controlador del evento puede invocar la devolución de llamada y actualizar la interfaz de usuario con los resultados.

+0

gracias por esta información. El problema es que mis callbacks se llaman desde el hilo que llama a 'io_service :: run', que no es el hilo principal de UI. Creo que voy a resolver esto teniendo un delegado proxy que use SendMessage para invocar a otro delegado en el hilo de UI. En cuanto a los hilos, los he usado. Sin embargo, utilicé 'io_service :: post (strand_.wrap (' en lugar de 'strand :: post', ¿esto es incorrecto? –

+0

@ Moo-Juice depende del contexto donde se usan, tanto el post como el wrap son muy similares –

+0

bueno, utilizo ** post ** en el nivel de socket, por ejemplo, cuando envío datos a una interfaz interna que inicia 'async_write_some'. Una abstracción de alto nivel (que pone en cola mandatos para ser transmitidos) utiliza el ** wrap ** en sus métodos, ya que no solo se puede publicar desde ningún hilo, sino que configura sus propios temporizadores de fecha límite (para evitar que la cola se envíe demasiado rápido). –

0

Cuando se reciben los datos, ¿cómo devuelve la gente los datos al subproceso de la interfaz de usuario? En el pasado, cuando usaba puertos de finalización, hice un evento especial que podría volver a publicar los datos en el hilo principal de UI usando un :: SendMessage. No era elegante, pero funcionó

:: PostMessage puede ser más adecuado.

A menos que todo se ejecute en un hilo, estos mecanismos se deben utilizar para publicar eventos de forma segura en el hilo de la interfaz de usuario.