Pregunta

Estoy escribiendo un servicio web ASP.NET usando C # que tiene una función DoLookup (). Para cada llamada a la función DoLookup () necesito mi código para ejecutar dos consultas separadas: una para otro servicio web en un sitio remoto y otra para una base de datos local. Ambas consultas deben completarse antes de que pueda compilar los resultados y devolverlos como respuesta al método DoLookup. El problema con el que estoy lidiando es que quiero que esto sea lo más eficiente posible, tanto en términos de tiempo de respuesta como de uso de recursos en el servidor web. Esperamos hasta varios miles de consultas por hora. Aquí hay una descripción general aproximada de C # de lo que tengo hasta ahora:

public class SomeService : System.Web.Services.WebService
{
    public SomeResponse DoLookup()
    {
        // Do the lookup at the remote web service and get the response 
        WebResponse wr = RemoteProvider.DoRemoteLookup();   

        // Do the lookup at the local database and get the response
        DBResponse dbr = DoDatabaseLookup();

        SomeResponse resp = new SomeResponse( wr, dbr);

        return resp;
    }
}

El código anterior hace todo secuencialmente y funciona muy bien, pero ahora quiero hacerlo más escalable. Sé que puedo llamar a la función DoRemoteLookup () de forma asíncrona (RemoteProvider tiene los métodos BeginRemoteLookup / EndRemoteLookup) y que también puedo hacer la búsqueda en la base de datos de forma asincrónica utilizando los métodos BeginExecuteNonQuery / EndExecuteNonQuery.

Mi pregunta (finalmente) es la siguiente: ¿cómo disparo tanto la búsqueda remota del servicio web como la búsqueda de la base de datos simultáneamente en hilos separados y me aseguro de que ambos se hayan completado antes de devolver la respuesta?

La razón por la que quiero ejecutar ambas solicitudes en subprocesos separados es que potencialmente tienen largos tiempos de respuesta (1 o 2 segundos) y me gustaría liberar los recursos del servidor web para manejar otras solicitudes mientras está esperando las respuestas. Una nota adicional: tengo la búsqueda remota de servicios web ejecutándose de forma asíncrona actualmente, simplemente no quise que la muestra anterior fuera demasiado confusa. Con lo que estoy luchando es conseguir que tanto la búsqueda remota de servicios Y la búsqueda en la base de datos se inicien al mismo tiempo y averiguar cuándo se han completado AMBOS.

Gracias por cualquier sugerencia.

¿Fue útil?

Solución

Puedes usar un par de AutoResetEvents , uno para cada hilo. Al final de la ejecución del subproceso, llama a AutoResetEvents.Set () para desencadenar el evento.

Después de generar los hilos, utiliza WaitAll () con los dos AutoResetEvents. Esto hará que el hilo se bloquee hasta que se configuren ambos eventos.

La advertencia de este enfoque es que debes asegurarte de que el Set () garantiza que se llamará, de lo contrario bloquearás para siempre. Además, asegúrese de que con los subprocesos ejerza el manejo adecuado de las excepciones, o causará inadvertidamente más problemas de rendimiento cuando las excepciones no manejadas hacen que su aplicación web se reinicie.

MSDN Tiene código de ejemplo sobre el uso de AutoResetEvent.

Otros consejos

Consulte Métodos asíncronos del servicio web XML , Cómo: Crear métodos de servicio web asíncronos y Cómo: Encadenar llamadas asíncronas con un método de servicio web .

Pero note el primer párrafo de esos artículos:

  

Este tema es específico de una tecnología heredada. Los clientes de servicios web XML y de servicios web XML ahora deben crearse utilizando Windows Communication Foundation (WCF) .


Por cierto, hacer las cosas como dicen estos artículos es importante porque libera el hilo de trabajo de ASP.NET mientras se ejecuta la tarea de larga duración. De lo contrario, es posible que esté bloqueando el subproceso de trabajo, evitando que atienda más solicitudes e impactando la escalabilidad.

Suponiendo que puede tener una función de devolución de llamada tanto para la solicitud web como para la búsqueda en la base de datos, algo en este sentido puede funcionar

bool webLookupDone = false;
bool databaseLookupDone = false;

private void FinishedDBLookupCallBack()
{
    databaseLookupDone = true;
    if(webLookupDone)
    {
        FinishMethod();
    }
}

private void FinishedWebLookupCallBack()
{
    webLookupDone = true;
    if(databaseLookupDone)
    {
        FinishMethod();
    }
}

Supongo que no tengo suficiente reputación para votar ni comentar. Así que este es un comentario sobre la respuesta de John Saunders y el comentario de Alan al respecto.

Definitivamente, usted quiere ir con la respuesta de John si le preocupa la escalabilidad y el consumo de recursos.

Hay dos consideraciones aquí: Acelerar una solicitud individual y hacer que su sistema maneje muchas solicitudes concurrentes de manera eficiente. La respuesta anterior tanto de Alan como de John se logra realizando las llamadas externas en paralelo.

Lo último, y parece que esa fue tu principal preocupación, se logra al no tener hilos bloqueados en ninguna parte, es decir, la respuesta de John.

No generes tus propios hilos. Los subprocesos son caros y ya hay muchos subprocesos en el IO Threadpool que manejará sus llamadas externas por usted si utiliza los métodos asíncronos proporcionados por el marco .net.

El método web de su servicio también debe ser asíncrono. De lo contrario, se bloqueará un subproceso de trabajo hasta que se realicen sus llamadas externas (aún es de 1-2 segundos, incluso si se ejecutan en paralelo). Y solo tiene 12 subprocesos por cada CPU que maneja las solicitudes entrantes (si su machine.config se establece de acuerdo con recomendación .) Es decir como mucho, podría manejar 12 solicitudes simultáneas (multiplicado por el número de CPU). Por otro lado, si su método web es asíncrono, el Begin volverá casi instantáneamente y el subproceso regresará al grupo de subprocesos de trabajo listo para manejar otra solicitud entrante, mientras que el puerto de finalización de IO esperará en sus llamadas externas. ser manejados por subprocesos del grupo de subprocesos IO, una vez que regresen.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top