Domanda

Sto scrivendo un servizio Web ASP.NET utilizzando C # che ha una funzione DoLookup (). Per ogni chiamata alla funzione DoLookup () ho bisogno del mio codice per eseguire due query separate: una a un altro servizio Web in un sito remoto e una a un database locale. Entrambe le query devono essere completate prima che io possa compilare i risultati e restituirli come risposta al metodo DoLookup. Il problema che sto affrontando è che voglio renderlo il più efficiente possibile, sia in termini di tempo di risposta che di utilizzo delle risorse sul web server. Ci aspettiamo fino a diverse migliaia di domande all'ora. Ecco una breve panoramica simile a C # di ciò che ho finora:

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;
    }
}

Il codice sopra fa tutto in modo sequenziale e funziona benissimo ma ora voglio renderlo più scalabile. So che posso chiamare la funzione DoRemoteLookup () in modo asincrono (RemoteProvider ha i metodi BeginRemoteLookup / EndRemoteLookup) e che posso anche fare la ricerca del database in modo asincrono usando i metodi BeginExecuteNonQuery / EndExecuteNonQuery.

La mia domanda (finalmente) è questa: come posso attivare sia la ricerca del servizio Web remoto che la ricerca del database contemporaneamente su thread separati e assicurarmi che siano stati completati entrambi prima di restituire la risposta?

Il motivo per cui voglio eseguire entrambe le richieste su thread separati è che entrambi hanno potenzialmente tempi di risposta lunghi (1 o 2 secondi) e vorrei liberare le risorse del server Web per gestire altre richieste mentre è in attesa di risposte. Un'ulteriore nota: attualmente la ricerca del servizio Web remoto è in esecuzione in modo asincrono, ma non volevo rendere l'esempio sopra troppo confuso. Ciò con cui sto lottando è ottenere sia la ricerca del servizio remoto sia la ricerca del database avviate contemporaneamente e capire quando hanno ENTRAMBI completato.

Grazie per eventuali suggerimenti.

È stato utile?

Soluzione

Puoi usare una coppia di AutoResetEvents , una per ogni thread. Alla fine dell'esecuzione del thread, si chiama AutoResetEvents.Set () per attivare l'evento.

Dopo aver generato i thread, si utilizza WaitAll () con i due AutoResetEvents. Ciò causerà il blocco del thread fino a quando non verranno impostati entrambi gli eventi.

L'avvertimento di questo approccio è che è necessario assicurarsi che Set () sia garantito per essere chiamato, altrimenti si bloccherà per sempre. Inoltre, assicurati che con i thread eserciti la corretta gestione delle eccezioni o causerai inavvertitamente maggiori problemi di prestazioni quando eccezioni non gestite causano il riavvio dell'applicazione web.

MSDN ha un codice di esempio relativo all'utilizzo di AutoResetEvent.

Altri suggerimenti

Vedi Metodi del servizio Web XML asincrono , Procedura: creare metodi di servizio Web asincroni e Procedura: concatenare le chiamate asincrone con un metodo di servizio Web .

Ma nota il primo paragrafo di quegli articoli:

  

Questo argomento è specifico di una tecnologia legacy. I servizi Web XML e i client dei servizi Web XML dovrebbero ora essere creati utilizzando Windows Communication Foundation (WCF) .


A proposito, fare le cose come dicono questi articoli è importante perché libera il thread di lavoro ASP.NET mentre viene eseguita l'attività di lunga durata. Altrimenti, potresti bloccare il thread di lavoro, impedendogli di soddisfare ulteriori richieste e influendo sulla scalabilità.

Supponendo che tu possa avere una funzione di callback sia per la richiesta web che per la ricerca nel database, allora qualcosa su queste linee potrebbe funzionare

bool webLookupDone = false;
bool databaseLookupDone = false;

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

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

Immagino di non avere abbastanza rappresentante per votare né commentare. Quindi questo è un commento sulla risposta di John Saunders e il commento di Alan su di esso.

Sicuramente vuoi andare con la risposta di John se sei preoccupato per la scalabilità e il consumo di risorse.

Ci sono due considerazioni qui: accelerare una singola richiesta e far sì che il sistema gestisca in modo efficiente molte richieste simultanee. La prima risposta sia di Alan che di John si ottiene eseguendo le chiamate esterne in parallelo.

Quest'ultimo, e sembra che questa fosse la tua principale preoccupazione, si ottiene non avendo i thread bloccati da nessuna parte, cioè la risposta di John.

Non generare i tuoi thread. I thread sono costosi e ci sono già molti thread in IO Threadpool che gestiranno le tue chiamate esterne se usi i metodi asincroni forniti dal framework .net.

Anche il metodo web del tuo servizio deve essere asincrono. In caso contrario, un thread di lavoro verrà bloccato fino al completamento delle chiamate esterne (restano comunque 1-2 secondi anche se vengono eseguiti in parallelo). E hai solo 12 thread per CPU che gestiscono le richieste in arrivo (se il tuo machine.config è impostato secondo raccomandazione .) Vale a dire al massimo saresti in grado di gestire 12 richieste simultanee (volte il numero di CPU). D'altra parte, se il tuo metodo web è asincrono, Begin tornerà all'istante e il thread tornerà al pool di thread di lavoro pronto a gestire un'altra richiesta in arrivo, mentre le tue chiamate esterne vengono attese dalla porta di completamento IO, dove verranno essere gestito dai thread dal pool di thread IO, una volta restituiti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top