Esporta (0) Stampa
Espandi tutto
Il presente articolo è stato tradotto automaticamente. Passare il puntatore sulle frasi nell'articolo per visualizzare il testo originale. Ulteriori informazioni.
Traduzione
Originale

Chiamata asincrona dei metodi sincroni

Con .NET Framework è possibile chiamare i metodi in modo asincrono. A tale scopo, definire un delegato con la stessa firma del metodo che si desidera chiamare. Common Language Runtime definirà automaticamente i metodi BeginInvoke e EndInvoke per il delegato, con le firme appropriate.

Nota Nota

Le chiamate asincrone dei delegati, in particolare dei metodi BeginInvoke e EndInvoke, non sono supportate in .NET Compact Framework.

Il metodo BeginInvoke avvia la chiamata asincrona e presenta gli stessi parametri del metodo che si desidera eseguire in modo asincrono, oltre a due parametri aggiuntivi facoltativi. Il primo parametro è un delegato AsyncCallback che fa riferimento a un metodo chiamato al completamento della chiamata asincrona. Il secondo parametro è un oggetto definito dall'utente che passa le informazioni al metodo di callback. BeginInvoke restituisce immediatamente un risultato senza attendere il completamento della chiamata asincrona. BeginInvoke restituisce un oggetto IAsyncResult che può essere utilizzato per monitorare l'avanzamento della chiamata asincrona.

Il metodo EndInvoke recupera i risultati della chiamata asincrona e può essere chiamato in qualsiasi momento dopo BeginInvoke. Se la chiamata asincrona non è stata completata, EndInvoke blocca il thread chiamante finché non viene completato. Nei parametri di EndInvoke sono inclusi quelli di tipo out e ref (<Out> ByRef e ByRef in Visual Basic) del metodo da eseguire in modo asincrono, oltre all'oggetto IAsyncResult restituito da BeginInvoke.

Nota Nota

In Visual Studio 2005 la funzionalità IntelliSense consente di visualizzare i parametri di BeginInvoke e EndInvoke. Se non si utilizza Visual Studio o uno strumento analogo o se si utilizza C# con Visual Studio 2005, vedere Modello di programmazione asincrona (APM) per una descrizione dei parametri definiti per tali metodi.

Negli esempi di codice riportati in questo argomento vengono illustrati quattro modi comuni di utilizzare BeginInvoke e EndInvoke per effettuare chiamate asincrone. Dopo aver chiamato BeginInvoke è possibile effettuare le operazioni seguenti:

  • Eseguire altre operazioni, quindi chiamare EndInvoke per arrestare l'esecuzione fino al completamento della chiamata.

  • Ottenere un oggetto WaitHandle mediante la proprietà IAsyncResult.AsyncWaitHandle, utilizzare il relativo metodo WaitOne per bloccare l'esecuzione fino alla segnalazione di WaitHandle e quindi chiamare EndInvoke.

  • Effettuare il polling dell'oggetto IAsyncResult restituito dal metodo BeginInvoke per determinare quando è stata completata la chiamata asincrona e quindi chiamare EndInvoke.

  • Passare un delegato per un metodo di callback a BeginInvoke. Il metodo viene eseguito su un thread ThreadPool al completamento della chiamata asincrona. Il metodo di callback chiama EndInvoke.

Nota importante Importante

Indipendentemente dalla tecnica utilizzata, chiamare sempre EndInvoke per completare la chiamata asincrona.

Negli esempi di codice seguenti vengono illustrati i diversi modi per chiamare in modo asincrono lo stesso metodo a esecuzione prolungata, ovvero TestMethod. Il metodo TestMethod visualizza un messaggio della console per indicare che l'elaborazione è iniziata, viene sospeso per alcuni secondi, quindi termina. TestMethod dispone di un parametro out che illustra il modo in cui tali parametri vengono aggiunti alle firme di BeginInvoke e EndInvoke. La gestione dei parametri ref può essere eseguita in modo analogo.

Nell'esempio di codice riportato di seguito viene illustrata la definizione di TestMethod e del delegato denominato AsyncMethodCaller che può essere utilizzato per chiamare il metodo TestMethod in modo asincrono. Per compilare gli esempi di codice, è necessario includere le definizioni di TestMethod e del delegato AsyncMethodCaller.


using System;
using System.Threading; 

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncDemo 
    {
        // The method to be executed asynchronously.
        public string TestMethod(int callDuration, out int threadId) 
        {
            Console.WriteLine("Test method begins.");
            Thread.Sleep(callDuration);
            threadId = Thread.CurrentThread.ManagedThreadId;
            return String.Format("My call time was {0}.", callDuration.ToString());
        }
    }
    // The delegate must have the same signature as the method
    // it will call asynchronously.
    public delegate string AsyncMethodCaller(int callDuration, out int threadId);
}


Il modo più semplice per eseguire un metodo in modo asincrono consiste nell'avviare l'esecuzione del metodo con una chiamata al metodo BeginInvoke del delegato, eseguire alcune operazioni sul thread principale, quindi chiamare il metodo EndInvoke del delegato. EndInvoke potrebbe bloccare il thread chiamante perché non restituisce un risultato fino al completamento della chiamata asincrona. Si tratta di una tecnica efficace da utilizzare con operazioni su file o di rete.

Nota importante Importante

Poiché EndInvoke potrebbe essere bloccato, non chiamarlo mai da thread dedicati all'interfaccia utente.


using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        public static void Main() 
        {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.",
                Thread.CurrentThread.ManagedThreadId);

            // Call EndInvoke to wait for the asynchronous call to complete,
            // and to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

Main thread 1 does some work.
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
 */


Per ottenere un oggetto WaitHandle, è possibile utilizzare la proprietà AsyncWaitHandle dell'oggetto IAsyncResult restituito da BeginInvoke. L'oggetto WaitHandle viene segnalato al completamento della chiamata asincrona ed è possibile attenderlo chiamando il metodo WaitOne.

Se si utilizza un oggetto WaitHandle, sarà possibile eseguire altre operazioni prima o dopo il completamento della chiamata asincrona, ma prima di recuperare i risultati chiamando EndInvoke.

Nota Nota

L'handle di attesa non viene chiuso automaticamente quando si chiama EndInvoke. Se si rilasciano tutti i riferimenti all'handle di attesa, le risorse di sistema vengono liberate quando la Garbage Collection recupera l'handle di attesa. Per liberare le risorse di sistema subito dopo aver terminato di utilizzare l'handle di attesa, eliminarlo con una chiamata al metodo WaitHandle.Close. La Garbage Collection funziona in modo più efficiente quando gli oggetti eliminabili vengono eliminati in modo esplicito.


using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() 
        {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.",
                Thread.CurrentThread.ManagedThreadId);

            // Wait for the WaitHandle to become signaled.
            result.AsyncWaitHandle.WaitOne();

            // Perform additional processing here.
            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            // Close the wait handle.
            result.AsyncWaitHandle.Close();

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

Main thread 1 does some work.
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
 */


Per rilevare il completamento della chiamata asincrona, è possibile utilizzare la proprietà IsCompleted dell'oggetto IAsyncResult restituito da BeginInvoke. Questo può essere fatto quando si effettua la chiamata asincrona da un thread dedicato all'interfaccia utente. Il polling del completamento consente di continuare a eseguire il thread chiamante mentre la chiamata asincrona viene eseguita su un thread ThreadPool.


using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

            // Poll while simulating work.
            while(result.IsCompleted == false) {
                Thread.Sleep(250);
                Console.Write(".");
            }

            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            Console.WriteLine("\nThe call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

Test method begins.
.............
The call executed on thread 3, with return value "My call time was 3000.".
 */


Se non è necessario che il thread che avvia la chiamata asincrona elabori i risultati, sarà possibile eseguire un metodo di callback al termine della chiamata. Il metodo di callback viene eseguito su un thread ThreadPool.

Per utilizzare un metodo di callback, è necessario passare a BeginInvoke un delegato AsyncCallback che faccia riferimento al metodo. È anche possibile passare un oggetto contenente informazioni che verranno utilizzate dal metodo di callback. Nel metodo di callback, è possibile eseguire il cast dell'oggetto IAsyncResult, che è l'unico parametro del metodo di callback, in un oggetto AsyncResult. È quindi possibile utilizzare la proprietà AsyncResult.AsyncDelegate per ottenere il delegato utilizzato per avviare la chiamata, in modo che sia possibile chiamare EndInvoke.

Nota sull'esempio:

  • Il parametro threadId di TestMethod è un parametro out (<Out> ByRef in Visual Basic), pertanto il valore di input non viene mai utilizzato da TestMethod. Alla chiamata a BeginInvoke viene passata una variabile fittizia. Se il parametro threadId fosse un parametro ref (ByRef in Visual Basic), la variabile dovrebbe essere un campo a livello di classe per poter essere passata sia a BeginInvoke sia a EndInvoke.

  • Le informazioni sullo stato passate a BeginInvoke sono una stringa di formato, utilizzata dal metodo di callback per formattare un messaggio di output. Poiché vengono passate come Object di tipo, è necessario eseguire il cast delle informazioni sullo stato nel tipo corretto prima di poterle utilizzare.

  • Il callback viene eseguito in un thread ThreadPool. I thread ThreadPool sono thread in background che non mantengono l'applicazione in esecuzione se il thread principale termina, quindi il thread principale dell'esempio deve rimanere sospeso per il tempo sufficiente per il completamento del callback.


using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() 
        {
            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // The threadId parameter of TestMethod is an out parameter, so
            // its input value is never used by TestMethod. Therefore, a dummy
            // variable can be passed to the BeginInvoke call. If the threadId
            // parameter were a ref parameter, it would have to be a class-
            // level field so that it could be passed to both BeginInvoke and 
            // EndInvoke.
            int dummy = 0;

            // Initiate the asynchronous call, passing three seconds (3000 ms)
            // for the callDuration parameter of TestMethod; a dummy variable 
            // for the out parameter (threadId); the callback delegate; and
            // state information that can be retrieved by the callback method.
            // In this case, the state information is a string that can be used
            // to format a console message.
            IAsyncResult result = caller.BeginInvoke(3000,
                out dummy, 
                new AsyncCallback(CallbackMethod),
                "The call executed on thread {0}, with return value \"{1}\".");

            Console.WriteLine("The main thread {0} continues to execute...", 
                Thread.CurrentThread.ManagedThreadId);

            // The callback is made on a ThreadPool thread. ThreadPool threads
            // are background threads, which do not keep the application running
            // if the main thread ends. Comment out the next line to demonstrate
            // this.
            Thread.Sleep(4000);

            Console.WriteLine("The main thread ends.");
        }

        // The callback method must have the same signature as the
        // AsyncCallback delegate.
        static void CallbackMethod(IAsyncResult ar) 
        {
            // Retrieve the delegate.
            AsyncResult result = (AsyncResult) ar;
            AsyncMethodCaller caller = (AsyncMethodCaller) result.AsyncDelegate;

            // Retrieve the format string that was passed as state 
            // information.
            string formatString = (string) ar.AsyncState;

            // Define a variable to receive the value of the out parameter.
            // If the parameter were ref rather than out then it would have to
            // be a class-level field so it could also be passed to BeginInvoke.
            int threadId = 0;

            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, ar);

            // Use the format string to format the output message.
            Console.WriteLine(formatString, threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

The main thread 1 continues to execute...
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
The main thread ends.
 */


Aggiunte alla community

AGGIUNGI
Mostra:
© 2014 Microsoft