2009-03-23 7 views
46

Ho un'operazione asincrona che per vari motivi deve essere attivata utilizzando una chiamata HTTP a una pagina Web ASP.NET. Quando viene richiesta la mia pagina, dovrebbe iniziare questa operazione e restituire immediatamente un riconoscimento al client.Esecuzione di un'operazione asincrona attivata da una richiesta di una pagina Web ASP.NET

Questo metodo è anche esposto tramite un servizio Web WCF e funziona perfettamente.

Al mio primo tentativo, un'eccezione è stato gettato, dicendomi:

Asynchronous operations are not allowed in this context. 
Page starting an asynchronous operation has to have the Async 
attribute set to true and an asynchronous operation can only be 
started on a page prior to PreRenderComplete event.

Così, naturalmente, ho aggiunto il parametro Async="true" alla direttiva @Page. Ora, non ricevo un errore, ma la pagina sta bloccando fino al completamento dell'operazione asincrona.

Come si attiva una vera e semplice pagina di attivazione e disattivazione?

Modifica: Alcuni codici per ulteriori informazioni. È un po 'più complicato di così, ma ho cercato di ottenere l'idea generale.

public partial class SendMessagePage : System.Web.UI.Page 
{ 
    protected void Page_Load(object sender, EventArgs e) 
    { 
     string message = Request.QueryString["Message"]; 
     string clientId = Request.QueryString["ClientId"]; 

     AsyncMessageSender sender = new AsyncMessageSender(clientId, message); 
     sender.Start(); 

     Response.Write("Success"); 
    } 
} 

La classe AsyncMessageSender:

public class AsyncMessageSender 
{ 
    private BackgroundWorker backgroundWorker; 
    private string client; 
    private string msg; 

    public AsyncMessageSender(string clientId, string message) 
    { 
     this.client = clientId; 
     this.msg = message; 

     // setup background thread to listen 
     backgroundThread = new BackgroundWorker(); 
     backgroundThread.WorkerSupportsCancellation = true; 
     backgroundThread.DoWork += new DoWorkEventHandler(backgroundThread_DoWork); 
    } 

    public void Start() 
    { 
     backgroundThread.RunWorkerAsync(); 
    } 

    ... 
    // after that it's pretty predictable 
} 
+0

Difficile rispondere senza visualizzare il codice che chiama questa operazione asincrona. – Bryan

+0

Ok, aggiornerò la domanda con maggiori informazioni. Grazie. – Damovisa

+0

In Azure, è possibile utilizzare i lavori Web. Vedere http://curah.microsoft.com/52143/using-the-webjobs-feature-of-windows-azure-web-sites I thread in background su ASP.NET sono problematici. – RickAndMSFT

risposta

28

Se non si cura di tornare nulla per l'utente, si può solo accendere o un thread separato, o per un approccio rapido e sporco, l'uso un delegato e invocarlo in modo asincrono. Se non ti interessa avvisare l'utente quando termina l'attività asincrona, puoi ignorare il callback. Prova a mettere un punto di interruzione alla fine del metodo SomeVeryLongAction(), e vedrete che termina in esecuzione dopo che la pagina è già stato servito:

private delegate void DoStuff(); //delegate for the action 

protected void Page_Load(object sender, EventArgs e) 
{ 

} 

protected void Button1_Click(object sender, EventArgs e) 
{ 
    //create the delegate 
    DoStuff myAction = new DoStuff(SomeVeryLongAction); 
    //invoke it asynchrnously, control passes to next statement 
    myAction.BeginInvoke(null, null); 
    Button1.Text = DateTime.Now.ToString(); 
} 


private void SomeVeryLongAction() 
{ 
    for (int i = 0; i < 100; i++) 
    { 
     //simulation of some VERY long job 
     System.Threading.Thread.Sleep(100); 
    } 
} 
+0

Grazie, BeginInvoke era la chiave - non sono sicuro del perché non l'ho provato! – Damovisa

+2

Pericolo però! Ignorando la richiamata, le risorse non verranno ripulite. Questa è una delle ragioni per cui ho consigliato un approccio diverso. Jeffrey Richter ne parla nel suo CLR tramite il libro C# (forse non ho il titolo giusto). –

+3

È EndInvoke che deve essere chiamato ad un certo punto per garantire la pulizia. Verissimo. Buona discussione: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/d88d3f1e-f4aa-40c1-b1b6-e79e801f3909/ http://msdn.microsoft.com/en- us/magazine/cc164036 (printer) .aspx –

25

OK, ecco il problema: l'attributo Async è per il caso in cui la tua pagina chiamerà qualche attività di lunga durata che blocca anche il thread, e quindi la tua pagina ha bisogno dell'output di quell'attività per restituire le informazioni all'utente. Ad esempio, se la tua pagina ha bisogno di chiamare un servizio web, aspetta la sua risposta, e poi usa i dati dalla risposta per rendere la tua pagina.

Il motivo per cui si utilizza l'attributo Async è di evitare il blocco del thread. Questo è importante perché le applicazioni ASP.NET utilizzano un pool di thread per servire le richieste e c'è solo un numero relativamente piccolo di thread disponibili. E se ogni chiamata lega il thread mentre attende la chiamata al servizio web, presto colpirà abbastanza utenti concorrenti che gli utenti dovranno attendere fino al completamento di queste chiamate al servizio web. L'attributo Async consente al thread di tornare al pool di thread e servire altri visitatori simultanei al tuo sito web, piuttosto che forzarlo a stare fermo senza fare nulla mentre attende che la chiamata al servizio web torni.

Il risultato per voi è questo: l'attributo Async è progettato per il caso in cui non è possibile eseguire il rendering della pagina fino al completamento dell'attività asincrona, ed è per questo che non esegue il rendering della pagina immediatamente.

È necessario avviare il proprio thread e renderlo un thread daemon. Non ricordo la sintassi esatta per quello, ma puoi facilmente trovarlo nel documento cercando nel documento BCL "demone". Ciò significa che il thread impedirà la chiusura della tua applicazione mentre è attiva, il che è importante perché ASP.NET e IIS si riservano il diritto di "riciclare il tuo processo" quando lo ritengono necessario, e se ciò accade mentre il tuo thread sta funzionando, il tuo compito verrà fermato. Fare in modo che il daemon del thread impedisca questo (eccetto alcuni possibili rari casi limite ... scoprirai di più quando trovi la documentazione su questo).

Quel thread daemon è il punto in cui inizierai queste attività. E dopo che hai detto al thread demone di eseguire l'operazione, puoi immediatamente rendere la tua pagina ... quindi il rendering della pagina avverrà immediatamente.

Ancora meglio di un thread daemon nel processo ASP.NET, tuttavia, sarebbe quello di implementare un servizio di Windows per eseguire l'attività. Richiedere all'applicazione ASP.NET di comunicare l'attività da eseguire al servizio. Non c'è bisogno di un thread daemon e non c'è bisogno di preoccuparsi che il processo ASP.NET venga riciclato. Come si dice al servizio di eseguire l'operazione? Forse tramite WCF, o forse inserendo un record in una tabella di database che il servizio esegue il polling. O in un certo numero di altri modi.

EDIT: Ecco un'altra idea, che ho usato prima per questo stesso scopo. Scrivi le informazioni sulla tua attività in una coda MSMQ. Avere un altro processo (magari anche su un'altra macchina) estrae da quella coda e svolgere l'attività dispendiosa in termini di tempo. Il lavoro di inserimento in una coda è ottimizzato per restituire il più rapidamente possibile, in modo che il thread non si blocchi mentre i dati inseriti nella coda vengono inviati attraverso il filo o qualcosa del genere. È uno dei modi più veloci per prendere nota del fatto che un'attività deve essere eseguita senza attendere che l'attività venga eseguita.

+0

Ottima risposta - grazie per quello. In realtà sto usando un po 'MSMQ in linea. Tutto quello che avevo davvero bisogno di fare era lanciare un altro thread per fare il mio lavoro come suggerivi. Come hai detto, il parametro Async nella direttiva @Page non mi avrebbe aiutato. – Damovisa

+0

Buono, felice che abbia aiutato. A proposito, assicuratevi di vedere il mio commento sull'altra risposta ... ignorando il lato EndInvoke delle cose causerà una perdita di risorse!E l'unico modo affidabile per essere sicuri di poter elaborare EndInvoke è di avere un thread daemon. Quindi sei tornato a quello o MSMQ/Servizio. –

+0

Grazie per quello - in realtà ho riutilizzato del codice che ha fatto una cosa simile e non ho fatto nulla su EndInvoke. – Damovisa

1

Se si riceve questo errore quando si chiama il servizio Web in modo asincrono, assicurarsi di aggiungere l'attributo Async = 'true' come indicato dal messaggio di eccezione ?

superiore della pagina < Page Language = 'VB' Async = 'true' AutoEventWireup = 'false' CodeFile = 'mynewpage.aspx.vb' eredita 'mynewpage' =%>

+0

non dimenticare di contrassegnare come ans se aiutato – Amrik

+3

risposta per questa domanda è stata accettata 3 anni fa. Anche Async = true è stato menzionato nella domanda originale. Benvenuti in SO. –

+0

@nathanchere se guardi da vicino ho risposto nel 2012. hai avuto la possibilità di votare giù chi ha copiato la mia risposta nel 2013 e ha accettato 14 volte? – Amrik

2

È possibile aggirare questa limitazione abbastanza facilmente e senza nemmeno impostare Async su true.

public void Start() 
{ 
    new Task(() => 
    { 
     backgroundThread.RunWorkerAsync(); 
    }).Start(); 
} 
21

webforms Se si esegue impostato Ansync = "true" nella vostra pagina aspx dove si stanno facendo la richiesta.
<%@ Page Language="C#" Async="true" ... %>

Problemi correlati