dotSITE
Шаблоны проектирования Work in Murano Software. Вопросы/Ответы
новости материалы решения форумы группы настройки/о проекте
Логин/Регистрация
Логин:
Пароль:
Запомнить вас:
Регистрация
Забыли пароль?

Комментарии

Рекомендации по асинхронному программированию

Цикл статей

Рекомендации по асинхронному программированию

Асинхронное программирование поддерживается многими областями общеязыковой среды выполнения, такими как Remoting, ASP.NET и Windows Forms. Асинхронное программирование является центральной идеей .NET Framework. В этом разделе приведены схемы разработки для асинхронного программирования.

Следует придерживаться следующих рекомендаций:

  • Клиент должен решить, должен ли отдельный вызов быть асинхронным.
  • Необязательно дополнительно программировать сервер, чтобы поддерживать асинхронное поведение его клиента. Среда выполнения сможет различить представления клиента и сервера. В результате исключена ситуация, в которой сервер должен реализовывать IDispatch и делать большую работу для поддержания динамических вызовов клиента.
  • Сервер может выбрать явную поддержку асинхронного поведения по двум причинам: или потому, что он может реализовать асинхронное поведение более эффективно, чем вся архитектура, или потому, что он захочет поддерживать только асинхронное поведение своими клиентами. Таким серверам для раскрытия асинхронных операций рекомендуется следовать схеме разработки, приведенной в этом документе.
  • Безопасность типов обязательна.
  • Среда выполнения предоставляет необходимые сервисы для поддержки модели асинхронного программирования. Эти сервисы включают следующее:

-                       Базовые элементы синхронизации, такие как критические секции и экземпляры ReaderWriterLock.

-                       Структурные компоненты синхронизации, такие как контейнеры, которые поддерживают метод WaitForMultipleObjects.

-                       Пулы потоков.

-                       Открытость базовой инфраструктуре, например, объектам Message и ThreadPool.

Схемы разработки для асинхронного программирования

Следующий пример демонстрирует серверный класс:

[C#]
public class PrimeFactorizer
{
   public bool Factorize(long factorizableNum,  
            ref long primefactor1,
            ref long primefactor2)
   {
      primefactor1 = 1;
      primefactor2 = factorizableNum;
 
      // Factorize using a low-tech approach.
      for (int i=2;i<factorizableNum;i++)
      {
         if (0 == (factorizableNum % i))
         {
            primefactor1 = i;
            primefactor2 = factorizableNum / i;
            break;
         }
      }
      if (1 == primefactor1 )
         return false;
      else
         return true   ;
   }
}

В следующем примере показан клиент, определяющий шаблон для асинхронного вызова метода Factorize из класса PrimeFactorizer предыдущего примера.

[C#]
// Define the delegate.
public delegate bool FactorizingCallback(long factorizableNum,  
      ref long primefactor1,
      ref long primefactor2);
 
// Create an instance of the Factorizer.
PrimeFactorizer pf = new PrimeFactorizer();
 
// Create a delegate on the Factorize method on the Factorizer.
FactorizingDelegate fd = new FactorizingDelegate(pf.Factorize);

Компилятор создаст следующий класс FactorizingCallback после синтаксического разбора его определения в первой строке предыдущего примера. Будут сгенерированы методы BeginInvoke и EndInvoke.

[C#]
public class FactorizingCallback : Delegate
{
   public bool Invoke(ulong factorizableNum,  
         ref ulong primefactor1, ref ulong primefactor2);
 
   // Supplied by the compiler.   
   public IAsyncResult BeginInvoke(ulong factorizableNum,  
            ref unsigned long primefactor1,
            ref unsigned long primefactor2, AsyncCallback cb, 
            Object AsyncState);
 
   // Supplied by the compiler.   
   public bool EndInvoke(ref ulong primefactor1,
               ref ulong primefactor2, IAsyncResult ar);
}

Интерфейс, используемый как параметр делегата в следующем примере, определен в библиотеке классов .NET Framework.. Подробнее смотри в разделе MSDN IAsyncResult Interface.

[C#]
public delegate AsyncCallback (IAsyncResult ar);
 
public interface IAsyncResult
{
   // Returns true if the asynchronous operation has completed.
   bool IsCompleted { get; }
 
   // Caller can use this to wait until operation is complete.
   WaitHandle AsyncWaitHandle { get; }
 
   // The delegate object for which the async call was invoked.
   Object AsyncObject { get; }
 
   // The state object passed in through BeginInvoke.
   Object AsyncState { get; }
 
   // Returns true if the call completed synchronously.
   bool CompletedSynchronously { get; }
}

Обратите внимание, что объект, реализующий интерфейс IAsyncResult, должен быть объектом ожидания (waitable) и его базовые элементы синхронизации должны быть уведомлен после отмены или завершения вызова. Это дает возможность клиенту ожидать окончания вызова, вместо проведения опроса. Среда выполнения поставляет большое количество объектов ожидания, которые зеркально отображают базовые элементы синхронизации Win32, такие как ManualResetEvent, AutoResetEvent и Mutex.

Метод Cancel – это запрос на отмену обработки метода после того, как закончился программный период ожидания. Обратите внимание, что это только запрос клиента, и серверу рекомендуется принимать его на обработку. Далее клиент не должен принимать того, что сервер полностью остановил обработку запроса после получения нотификации об отмене метода. Другими словами, клиенту рекомендуется не уничтожать такие ресурсы, как объекты файла, потому что сервер в данный момент может активно их использовать. Свойству IsCanceled будет присвоено значение true, если вызов был отменен, и свойству IsCompleted будет присвоено значение true после того, как сервер закончит обработку вызова. После того, как сервер присвоит свойству IsCompleted значение true, сервер не сможет использовать никакие предоставленные клиентом ресурсы вне обусловленной семантики совместного использования. Таким образом, клиент может безопасно уничтожать ресурсы после того, как свойство IsCompleted возвращает true.

Свойство Server возвращает серверный объект, который  предоставил IAsyncResult.

Следующий пример демонстрирует программную модель со стороны клиента для асинхронного вызова метода Factorize.

[C#]
public class ProcessFactorizeNumber
{
   private long _ulNumber;
 
   public ProcessFactorizeNumber(long number)
   {
      _ulNumber = number;
   }
 
   [OneWayAttribute()]
   public void FactorizedResults(IAsyncResult ar)
   {
      long factor1=0, factor2=0; 
      
      // Extract the delegate from the AsynchResult.
      FactorizingCallback fd = 
         (FactorizingCallback) ((AsyncResult)ar).AsyncDelegate;
      // Obtain the result.
      fd.EndInvoke(ref factor1, ref factor2, ar);
 
      // Output the results.
      Console.Writeline("On CallBack: Factors of {0} : {1} {2}",
                        _ulNumber, factor1, factor2);
   }
}
 
// Async Variation 1.
// The ProcessFactorizeNumber.FactorizedResults callback function
// is called when the call completes.
public void FactorizeNumber1()
{
   // Client code.
   PrimeFactorizer pf = new PrimeFactorizer();
   FactorizingCallback fd = new FactorizingCallback(pf.Factorize);
 
   long factorizableNum = 1000589023, temp=0; 
 
   // Create an instance of the class that
   // will be called when the call completes.
   ProcessFactorizedNumber fc = 
      new ProcessFactorizedNumber(factorizableNum);
   // Define the AsyncCallback delegate.   
   AsyncCallbackDelegate cb = new AsyncCallback(fc.FactorizedResults);
   // Any object can be the state object.
   Object state = new Object();
 
   // Asynchronously invoke the Factorize method on pf.
   // Note: If you have pure out parameters, you do not need the 
   // temp variable.
   IAsyncResult ar = fd.BeginInvoke(factorizableNum, ref temp, ref temp,  
                        cb, state); 
 
// Proceed to do other useful work.
 
// Async Variation 2.
// Waits for the result.
// Asynchronously invoke the Factorize method on pf.
// Note: If you have pure out parameters, you do not need 
// the temp variable.
public void FactorizeNumber2()
{
   // Client code.
   PrimeFactorizer pf = new PrimeFactorizer();
   FactorizingCallback fd = new FactorizingCallback(pf.Factorize);
 
   long factorizableNum = 1000589023, temp=0;
   // Create an instance of the class 
   // to be called when the call completes.
   ProcessFactorizedNumber fc = 
            new ProcessFactorizedNumber(factorizableNum);
 
   // Define the AsyncCallback delegate.
   AsyncCallback cb = new AsyncCallback(fc.FactorizedResults);
 
   // Any object can be the state object.
   Object state = new Object();
 
   // Asynchronously invoke the Factorize method on pf.
   IAsyncResult ar = fd.BeginInvoke(factorizableNum, ref temp, ref temp, 
                        null, null); 
 
   ar.AsyncWaitHandle.WaitOne(10000, false);
 
   if(ar.IsCompleted)
   {
      int factor1=0, factor2=0;
 
      // Obtain the result.
      fd.EndInvoke(ref factor1, ref factor2, ar);
 
      // Output the results.
      Console.Writeline("Sequential : Factors of {0} : {1} {2}", 
                     factorizableNum, factor1, factor2);
   }
}

Обратите внимание, что если FactorizeCallback является контекстно-ограниченным классом, который требует синхронизированного или потоко-родственного контекста, функция обратного вызова отправляется через инфраструктуру диспетчера контекста. Иначе говоря, сама функция обратного вызова для таких контекстов может выполняться асинхронно в зависимости от ее вызывающей функции. Это семантика одностороннего классификатора сигнатур метода. Любой такой вызов метода может выполняться синхронно или асинхронно в зависимости от вызывающей функции, и вызывающая функция не может делать никаких предположений о завершении такого вызова, когда контроль за выполнением возвращается к нему.

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

Заключение

Сервер разбивает любую асинхронную операцию на две логические части: часть, которая принимает входной сигнал от клиента и начинает асинхронную операцию, и часть, которая поставляет результаты асинхронной операции клиенту. Кроме входного сигнала, необходимого асинхронной операции, первая часть также выбирает объект AsyncCallbackDelegate, который должен будет вызываться после завершения асинхронной операции. Первая часть возвращает объект ожидания, который реализует интерфейс IAsyncResult, используемый клиентом для определения статуса асинхронной операции. Обычно сервер также использует объект ожидания, который он возвращает клиенту, чтобы поддерживать любое состояние, связанное с асинхронной операцией. Клиент использует вторую часть, чтобы получить результаты асинхронной операции, поставляя объект ожидания.

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

Клиенту доступны следующие варианты завершения асинхронных операций:

  • Опросите возвращенный объект IAsyncResult на наличие завершения.
  • Попытайтесь завершить операцию преждевременно, таким образом создавая блокировку до тех пор, пока операция завершается.
  • Ожидайте объект IAsyncResult. Различие между этой и предыдущим вариантом в том, что клиент может использовать ожидания для того, чтобы периодически получать контроль выполнения.
  • Завершите операцию внутри процедуры функции обратного вызова.

Один сценарий, в котором желательны и синхронные, и асинхронные методы чтения и записи, - это использование ввода/вывода (input/output) файла. Следующий пример иллюстрирует схему разработки, показывая как объект File реализует операции чтения и записи.

[C#]
public class File
{
   // Other methods for this class go here.
 
   // Synchronous read method.
   long Read(Byte[] buffer, long NumToRead);
 
   // Asynchronous read method.
   IAsyncResult BeginRead(Byte[] buffer, long NumToRead, 
            AsyncCallbackDelegate cb);
   long EndRead(IAsyncResult ar);
 
   // Synchronous write method.
   long Write(Byte[] buffer, long NumToWrite);
 
   // Asynchrnous write method.
   IAsyncResult BeginWrite(Byte[] buffer, long NumToWrite, 
            AsyncCallbackDelegate cb);
 
   long EndWrite(IAsyncResult ar);
}
Клиент не может просто ассоциировать состояние с данной асинхронной операцией без определения нового делегата функции обратного вызова для каждой операции. Этот недостаток может быть устранен путем вынуждения методов Begin, таких как BeginWrite, принимать параметр дополнительного объекта, который представляет состояние.


Контакт Реклама на сайте Спонсорам Веб мастерам

Лицензионное соглашение - © 2000-2010 dotSITE
Хостинг .NET предоставлен PARKING.RU
Поддержку сайта осуществляет Murano Software Inc., Offshore software development