Экспорт (0) Печать
Развернуть все
Данная статья переведена автоматически. Наведите указатель мыши на предложения статьи, чтобы просмотреть исходный текст. Дополнительные сведения.
Перевод
Текст оригинала

Асинхронный вызов синхронных методов

В .NET Framework можно асинхронно вызывать любой метод. Для этого необходимо определить делегат с той же сигнатурой, что и у вызываемого метода. Среда CLR автоматически определяет для этого делегата методы BeginInvoke и EndInvoke с соответствующими сигнатурами.

Примечание Примечание

Асинхронные вызовы делегатов, в частности методы BeginInvoke и EndInvoke, не поддерживаются в .NET Compact Framework.

Асинхронный вызов инициируется с помощью метода BeginInvoke. Он имеет те же параметры, что и метод, который нужно выполнить асинхронно, а также два дополнительных параметра. Первый параметр является делегатом AsyncCallback, который ссылается на метод, вызываемый при завершении асинхронного вызова. Второй параметр — это пользовательский объект, который передает данные в метод обратного вызова. Метод BeginInvoke выполняет возврат немедленно, без ожидания завершения асинхронного вызова. Метод BeginInvoke возвращает объект IAsyncResult, который можно использовать для отслеживания выполнения асинхронного вызова.

Метод EndInvoke извлекает результаты асинхронного вызова. Его можно вызвать в любое время после вызова метода BeginInvoke. Если асинхронный вызов не завершен, метод EndInvoke блокирует вызывающий поток до завершения вызова. Список параметров EndInvoke включает параметры out и ref (<Out> ByRef и ByRef в Visual Basic) метода, который требуется вызвать асинхронно, а также значение IAsyncResult, возвращаемое методом BeginInvoke.

Примечание Примечание

Функция IntelliSense в Visual Studio 2005 отображает параметры методов BeginInvoke и EndInvoke. Если не используется Visual Studio или похожий инструмент или если используется C# вместе с Visual Studio 2005, обратитесь к разделу Асинхронная модель программирования (APM) для получения описания параметров, определенных для этих методов.

В приведенных в этом разделе примерах кода демонстрируются четыре основных способа использования методов BeginInvoke и EndInvoke для выполнения асинхронных вызовов. После вызова метода BeginInvoke можно делать следующее.

  • Выполнить какие-либо операции, а затем вызвать метод EndInvoke для блокировки потока до тех пор, пока вызов не завершится.

  • Получить объект WaitHandle с помощью свойства IAsyncResult.AsyncWaitHandle, использовать метод WaitOne для блокирования выполнения до получения сигнала WaitHandle, а затем вызвать метод EndInvoke.

  • Периодически опрашивать интерфейс IAsyncResult, возвращаемый методом BeginInvoke, для определения момента завершения асинхронного вызова, и затем вызвать метод EndInvoke.

  • Передать в метод BeginInvoke делегат для метода обратного вызова. Этот метод выполняется для потока ThreadPool после выполнения асинхронного вызова. Метод обратного вызова вызывает метод EndInvoke.

Важное примечание Важно

Независимо от выбранного варианта необходимо всегда использовать для завершения асинхронного вызова метод EndInvoke.

В приведенных ниже примерах кода показаны различные способы асинхронного вызова одного и того же долгосрочного метода TestMethod. Метод TestMethod отображает сообщение консоли, указывающее на начало выполнения, бездействует несколько секунд и завершается. В методе TestMethod имеется параметр out для демонстрации порядка добавления таких параметров в сигнатуры методов BeginInvoke и EndInvoke. Таким же способом можно обрабатывать параметры ref.

В следующем примере кода показано определение TestMethod и делегата AsyncMethodCaller, который может использоваться для асинхронного вызова TestMethod. Чтобы скомпилировать эти примеры кода, необходимо включить определения для метода TestMethod и делегата 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);
}


Самым простым способом асинхронного вызова метода является запуск выполнения метода посредством вызова метода BeginInvoke делегата, выполнения каких-либо действий с основным потоком и последующего вызова метода EndInvoke делегата. Метод EndInvoke может блокировать вызывающий поток, поскольку он не возвращается до завершения асинхронного вызова. Этот подход хорошо использовать с файловыми и сетевыми операциями.

Важное примечание Важно

Поскольку метод EndInvoke может заблокировать поток, его ни в коем случае нельзя вызывать из потоков, обслуживающих пользовательский интерфейс.


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.".
 */


Объект WaitHandle можно получить с помощью свойства AsyncWaitHandle объекта IAsyncResult, возвращаемого методом BeginInvoke. Объект WaitHandle получает сигнал при завершении асинхронного вызова; его можно дождаться путем вызова метода WaitOne.

При использовании объекта WaitHandle можно выполнять дополнительные операции до или после завершения асинхронного вызова, но до вызова метода EndInvoke для получения результатов.

Примечание Примечание

При вызове метода EndInvoke дескриптор ожидания не закрывается автоматически. Если удалить все ссылки на дескриптор ожидания, системные ресурсы будут освобождены при удалении дескриптора ожидания сборщиком мусора. Чтобы освободить системные ресурсы сразу после завершения использования дескриптора ожидания, удалите его с помощью метода WaitHandle.Close. При явном удалении ненужных объектов сборщик мусора работает более эффективно.


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.".
 */


Свойство IsCompleted объекта IAsyncResult, возвращаемого методом BeginInvoke, можно использовать для отслеживания завершения асинхронного метода. Это можно делать, когда асинхронный вызов произведен из потока, обслуживающего пользовательский интерфейс. Опрос завершения позволяет вызывающему потоку продолжить выполнение при асинхронном вызове потока 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.".
 */


Если поток, инициировавший асинхронный вызов, не обязательно должен быть потоком, обрабатывающим результаты вызова, после завершения асинхронного вызова можно выполнить метод обратного вызова. Метод обратного вызова выполняется для потока ThreadPool.

Чтобы использовать метод обратного вызова, необходимо передать в метод BeginInvoke делегат AsyncCallback, который ссылается на метод обратного вызова. Кроме того, можно передать объект, содержащий данные, которые будут использоваться методом обратного вызова. В методе обратного вызова параметр IAsyncResult, который является единственным параметром метода обратного вызова, можно привести к типу объекта AsyncResult. После этого свойство AsyncResult.AsyncDelegate можно будет использовать для получения делегата, с помощью которого инициирован вызов, чтобы можно было вызвать метод EndInvoke.

Примечания к примеру.

  • Параметр threadId метода TestMethod является параметром out (<Out> ByRef в Visual Basic), поэтому входные значения никогда не используются методом TestMethod. При вызове метода BeginInvoke ему передается фиктивный параметр. Если параметр threadId является параметром ref (ByRef в Visual Basic), переменная должна быть полем уровня класса, чтобы ее можно было передавать методам BeginInvoke и EndInvoke.

  • Сведения о состоянии передаются методу BeginInvoke в виде строки форматирования, используемую методом обратного вызова для форматирования выходного сообщения. Поскольку сведения о состоянии передаются в виде типа Object, перед использованием их необходимо привести к соответствующему типу.

  • Обратный вызов выполняется в потоке ThreadPool. Потоки ThreadPool — это фоновые потоки, которые не могут поддерживать приложение в рабочем состоянии при завершении основного потока, поэтому основной поток в приведенном примере должен дождаться завершения обратного вызова.


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.
 */


Добавления сообщества

ДОБАВИТЬ
Показ:
© 2015 Microsoft