2012-03-26 7 views
5

La función WaitNamedPipe permite que una aplicación de cliente de tubería espere sincrónicamente una conexión disponible en un servidor de canalización con nombre. Luego, llama al CreateFile para abrir el conducto como cliente. Pseudocódigo:¿Qué es una alternativa de E/S superpuesta a WaitNamedPipe?

// loop works around race condition with WaitNamedPipe and CreateFile 
HANDLE hPipe; 
while (true) { 
    if (WaitNamedPipe says connection is ready) { 
     hPipe = CreateFile(...); 
     if (hPipe ok or last error is NOT pipe busy) { 
      break; // hPipe is valid or last error is set 
     } 
    } else { 
     break; // WaitNamedPipe failed 
    } 
} 

El problema es que todas estas son llamadas de bloqueo y síncronas. ¿Cuál es una buena manera de hacer esto de forma asíncrona? Parece que no puedo encontrar una API que use E/S superpuestas para hacer esto, por ejemplo. Por ejemplo, para los servidores de tubería , la función ConnectNamedPipe proporciona un parámetro lpOverlapped que permite a un servidor esperar asíncronamente a un cliente. El servidor de tuberías puede llamar al WaitForMultipleObjects y esperar a que se complete la operación de E/S o que se marque cualquier otro evento (por ejemplo, un evento que indica el hilo para cancelar la E/S pendiente y terminar).

La única forma en que se me ocurre es llamar al WaitNamedPipe en un bucle con un tiempo de espera corto y finito y verificar otras señales si se agota el tiempo. Alternativamente, en una llamada de bucle CreateFile, verifique otras señales y luego llame al Sleep con un breve retraso (o WaitNamedPipe). Por ejemplo:

HANDLE hPipe; 
while (true) { 
    hPipe = CreateFile(...); 
    if (hPipe not valid and pipe is busy) { 
     // sleep 100 milliseconds; alternatively, call WaitNamedPipe with timeout 
     Sleep(100); 
     // TODO: check other signals here to see if we should abort I/O 
    } else 
     break; 
} 

Pero este método apesta al cielo en mi opinión. Si un tubo no está disponible por un tiempo, el hilo continúa funcionando: chupando CPU, usando energía, requiriendo que las páginas de memoria permanezcan en RAM, etc. En mi opinión, un hilo que se basa en Sleep o corto tiempo de espera no funciona bien y es un signo de programación descuidada de múltiples hilos.

¿Pero cuál es la alternativa en este caso?

+1

Una solución obvia es poner la llamada a WaitNamedPipe en un hilo separado. IIRC, varias de las funciones asíncronas de IO en realidad usan subprocesos bajo el capó, por lo que esto no es tan ineficiente como parece. –

+0

Cuéntanos más acerca de la condición de carrera que este código está trabajando. El código de cliente de muestra de la documentación de MS dice que primero llame a CreateFile y solo llama a WaitNamedPipe si Create falla con Pipe Ocupado. Hazlo en un bucle, con un tiempo de espera apropiado en Espera. Eso siempre ha funcionado en mi experiencia. El error solo ocurre cuando dos clientes van por una tubería, pero no es una carrera. Un cliente se conecta, y el otro espera y vuelve a intentar, que es lo mismo que sucede con su ejemplo de suspensión, pero más limpio. –

+1

@Mark: está en la documentación. Cuando WaitNamedPipe tiene éxito, CreateFile aún puede fallar porque otro hilo saltó primero. Esa es una condición de carrera: dos o más hilos están corriendo para abrir la tubería. Poner el lazo en funciona alrededor de la condición de carrera, de ahí el comentario en el código OPs. Sin embargo, ese no es el problema que nos está pidiendo que solucionemos. –

Respuesta

4

WaitNamedPipe es completamente inútil, y usará toda la CPU si especifica un tiempo de espera y no hay ningún servidor esperando por ella.

Simplemente llame al CreateFile una y otra vez con un Sleep como lo está haciendo, y muévalo a otros hilos como considere apropiado. No hay una alternativa API.

El único "beneficio" WaitNamedPipe que se proporciona es si desea saber si puede conectarse a un conducto con nombre pero explícitamente no desea abrir una conexión. Es basura.

Si realmente quiere ser exhaustiva, sus únicas opciones son

  • Asegúrese de que cualquier programa es la apertura de la canalización con nombre siempre está llamando CreateNamedPipe de nuevo inmediatamente después de que se nombra tubería está conectada a.
  • Haga que su programa realmente compruebe si ese programa se está ejecutando.
  • Si su intención es realmente no tener conexiones adicionales, todavía llame al CreateNamedPipe, y cuando alguien se conecta, dígales que se vayan hasta que se les espere un tiempo determinado, el cierre de la tubería.
+0

Supongo que solo usa toda la CPU si nada más está compitiendo? Al menos debería estar cediendo la CPU una vez por cada paso del reloj. Esto realmente tiene sentido, significa que el protocolo subyacente no proporciona ninguna forma de poner en cola las conexiones, lo que explica por qué no hay una versión asíncrona. Es lo mismo con las conexiones TCP. Si el servidor no está escuchando, no hay forma de decir "Ah, vale, llámame cuando estés libre", solo tienes que volver a intentarlo periódicamente. –

+0

Es solo negligencia de Microsoft. Cuando llames a 'WaitNamedPipe' con un tiempo de espera, una de tus cpus estará en% 100 hasta que regrese - se llama livelock - se permitirán otras cosas, pero no habrá ciclos inactivos en esa CPU. Sospecho que lo remediarán dentro de una década o dos. – fcrick

+0

Terminé usando solo un tiempo de espera limitado. –

2

¿Por qué el servidor no puede crear más tuberías? El rendimiento alcanzado en el escenario que describes no es un problema si es raro.

I.e. si hay suficientes tuberías para todos, ¿qué importa si usa CreateFile/Sleep en lugar de WaitForMultipleObjects? El golpe de rendimiento no tendrá importancia.

También tengo que cuestionar la necesidad de IO superpuesto en un cliente. ¿Con cuántos servidores se está comunicando a la vez? Si la respuesta es inferior a, por ejemplo, 10, podría razonablemente crear un hilo por conexión.

Básicamente digo que creo que la razón por la que no hay superposición WaitforNamedPipe es porque no existe un uso razonable que lo requiera.

+0

Los clientes no tienen control sobre los servidores y deben suponer lo peor. (En este caso particular, el servidor solo maneja un cliente a la vez, pero creo que incluso si un servidor manejara varios clientes a la vez, los clientes deberían estar preparados para un servidor completo.) –

+0

@JamesJohnston: ¿no está escribiendo el código del servidor? Las tuberías con nombre son utilizadas principalmente por clientes y servidores escritos juntos. –

+0

@Ben El caso de uso que requiere una versión IO solapada de WaitNamedPipe es donde el cliente desea hacer algo mientras espera, pero poner la espera en su propio hilo no es una opción. ¡Ese no es un caso de uso poco común! –

Cuestiones relacionadas