Синхронные и асинхронные операции

В этом разделе описывается реализация и вызов асинхронных операций службы.

Многие приложения вызывают методы асинхронно, поскольку это позволяет приложению продолжать выполнение других операций, пока осуществляется вызов метода. Службы и клиенты WCF (Windows Communication Foundation) могут участвовать в асинхронных вызовах операций на двух различных уровнях приложения, которые обеспечивают приложениям WCF большую гибкость для увеличения пропускной способности без ущерба для интерактивности.

Типы асинхронных операций

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

У клиента имеется только контракт службы, который задает шаблон обмена сообщениями для конкретной операции. Клиенты могут предложить разработчикам любую модель программирования, если соблюдается соответствующий шаблон обмена сообщениями. Аналогично службы могут реализовывать операции произвольным образом, пока соблюдается заданный шаблон обмена сообщениями.

Независимость контракта службы от реализации службы и клиента делает возможными следующие формы асинхронного выполнения в приложениях WCF:

  • клиенты могут вызывать операции запроса и ответа асинхронно, используя синхронный механизм обмена сообщениями;

  • службы могут реализовывать операции запроса и ответа асинхронно, используя синхронный механизм обмена сообщениями;

  • обмен сообщениями может быть односторонним независимо от реализации клиента или службы.

Предлагаемые асинхронные сценарии

Асинхронный подход следует использовать при реализации операции службы, если реализация операции службы осуществляет блокирующий вызов, например операцию ввода-вывода. При реализации асинхронной операции попробуйте вызывать асинхронные операции и методы, чтобы, насколько это возможно, расширить функции асинхронного вызова. Например вызовите метод BeginOperationTwo() из метода BeginOperationOne().

  • Асинхронный подход следует использовать в клиенте или вызывающем приложении в следующих случаях.

  • Если операции вызываются из приложения промежуточного уровня. (Дополнительные сведения о таких сценариях см. в статье о клиентских приложениях среднего уровня.)

  • Если операции вызываются на страницах ASP.NET, следует использовать асинхронные страницы.

  • Если операции вызываются из любого однопотокового приложения, например приложения Windows Forms или WCF (Windows Presentation Foundation). При использовании модели асинхронных вызовов на основе событий результирующее событие создается в потоке пользовательского интерфейса, в результате чего приложение получает возможность реагирования на действия пользователя, но при этом не требуется управлять несколькими потоками.

  • В общем случае при выборе между синхронным и асинхронным вызовом следует выбирать асинхронный вызов.

Реализация асинхронной операции службы

Асинхронные операции могут быть реализованы с помощью одного из трех следующих методов:

  1. асинхронная модель на основе задач;

  2. асинхронная модель на основе событий;

  3. асинхронная модель IAsyncResult.

Асинхронная модель на основе задач

Асинхронная модель на основе задач - это предпочтительный способ реализации асинхронных операций в силу его чрезвычайного удобства и простоты. Чтобы использовать этот метод, просто реализуйте операцию службы и укажите тип возвращаемой задачи<T>, где T — тип, возвращаемый логической операцией. Например:

public class SampleService:ISampleService
{
   // ...  
   public async Task<string> SampleMethodTaskAsync(string msg)
   {
      return Task<string>.Factory.StartNew(() =>
      {
         return msg;
      });
   }  
   // ...  
}  

Операция SampleMethodTaskAsync возвращает строку> задачи<, так как логическая операция возвращает строку. Дополнительные сведения об асинхронной модели на основе задач см. в этой статье.

Предупреждение

При использовании асинхронной модели на основе задач может быть вызван объект T:System.AggregateException в случае возникновения исключения во время ожидания завершения операции. Это исключение может возникнуть на стороне клиента или службы

Асинхронная модель на основе событий

Служба, поддерживающая асинхронную модель на основе событий, будет содержать одну или несколько операций с именем MethodNameAsync. Эти методы могут копировать синхронные версии, выполняющие ту же операцию в текущем потоке. Этот класс также может содержать событие MethodNameCompleted, а также метод MethodNameAsyncCancel (или просто CancelAsync). Клиент, вызывающий операцию, определяет обработчик событий, вызываемый после завершения операции.

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

public class AsyncExample  
{  
    // Synchronous methods.  
    public int Method1(string param);  
    public void Method2(double param);  
  
    // Asynchronous methods.  
    public void Method1Async(string param);  
    public void Method1Async(string param, object userState);  
    public event Method1CompletedEventHandler Method1Completed;  
  
    public void Method2Async(double param);  
    public void Method2Async(double param, object userState);  
    public event Method2CompletedEventHandler Method2Completed;  
  
    public void CancelAsync(object userState);  
  
    public bool IsBusy { get; }  
  
    // Class implementation not shown.  
}  

Дополнительные сведения об асинхронной модели на основе событий см. в этой статье.

Асинхронная модель IAsyncResult

Операция службы может быть реализована в асинхронном режиме с помощью платформа .NET Framework асинхронного шаблона программирования и маркировки <Begin> метода с заданным trueсвойствомAsyncPattern. В этом случае асинхронная операция доступна в метаданных так же, как и синхронная операция: она предоставляется в виде одной операции с сообщением запроса и согласованным с ним сообщением ответа. В этом случае имеется возможность выбора одной из двух моделей программирования клиента. Этот шаблон может быть представлен в них в виде синхронной или асинхронной операции, поскольку при вызове службы имеет место обмен сообщениями "запрос-ответ".

В целом в связи с асинхронной природой систем полагаться на потоки не следует. Самый надежный способ передачи данных на различные этапы обработки диспетчеризации операций - это использование расширений.

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

Чтобы определить операцию контракта X, которая выполняется асинхронно независимо от того, как она вызывается в клиентском приложении, выполните следующие действия:

  • Определите два метода, используя шаблон BeginOperation и EndOperation.

  • Метод BeginOperation включает параметры in и ref для операции и возвращает значение типа IAsyncResult.

  • Метод EndOperation включает параметр IAsyncResult, а также параметры out и ref, и возвращает результат возвращаемого типа операции.

См., например, следующий метод.

int DoWork(string data, ref string inout, out string outonly)  
Function DoWork(ByVal data As String, ByRef inout As String, _out outonly As out) As Integer  

Для создания асинхронной операции эти два метода должны иметь следующий вид.

[OperationContract(AsyncPattern=true)]
IAsyncResult BeginDoWork(string data,
                         ref string inout,
                         AsyncCallback callback,
                         object state);
int EndDoWork(ref string inout, out string outonly, IAsyncResult result);  
<OperationContract(AsyncPattern := True)>
Function BeginDoWork(ByVal data As String, _
                     ByRef inout As String, _
                     ByVal callback As AsyncCallback, _
                     ByVal state As Object) As IAsyncResult
Function EndDoWork(ByRef inout As String, ByRef outonly As String, ByVal result As IAsyncResult) As Integer  

Примечание.

Атрибут OperationContractAttribute применяется только к методу BeginDoWork. В получаемом контракте имеется одна операция WSDL с именем DoWork.

Асинхронные вызовы на стороне клиента

Клиентское приложение WCF может использовать любую из трех описанных выше моделей асинхронных вызовов.

При использовании модели на основе задач просто вызовите операцию, используя ключевое слово «await», как показано в следующем фрагменте кода.

await simpleServiceClient.SampleMethodTaskAsync("hello, world");  

Использование асинхронной модели на основе событий требует добавления обработчика событий для получения уведомлений об ответе. Результирующее событие будет создано в потоке пользовательского интерфейса автоматически. Чтобы воспользоваться этим подходом, укажите параметры командной строки /async и /tcv:Version35 в служебном средстве ServiceModel Metadata Utility Tool (Svcutil.exe), как показано в следующем примере.

svcutil http://localhost:8000/servicemodelsamples/service/mex /async /tcv:Version35  

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

Однако асинхронная модель на основе событий доступна только в платформа .NET Framework 3.5. Кроме того, он не поддерживается даже в платформа .NET Framework 3.5 при создании клиентского канала WCF с помощью System.ServiceModel.ChannelFactory<TChannel>. Для асинхронного вызова операций в объектах клиентского канала WCF необходимо использовать объекты System.IAsyncResult. Чтобы воспользоваться этим подходом, укажите параметр командной строки /async в служебном средстве ServiceModel Metadata Utility Tool (Svcutil.exe), как показано в следующем примере.

svcutil http://localhost:8000/servicemodelsamples/service/mex /async

В результате будет создан контракт службы, в котором каждая операция моделируется в виде метода <Begin>, где свойство AsyncPattern имеет значение true, и соответствующего метода <End>. Полный пример с использованием ChannelFactory<TChannel> см. в статье Практическое руководство. Асинхронный вызов операций с использованием фабрики каналов.

В любом случае приложения могут вызывать операцию асинхронно, даже если служба реализована синхронно, так же, как приложение может использовать один и тот же шаблон для асинхронного вызова и вызова локального синхронного метода. Реализация операции не является значительной для клиента; При поступлении сообщения ответа его содержимое отправляется в асинхронный <End> метод клиента, а клиент получает информацию.

Шаблоны одностороннего обмена сообщениями

Можно создать асинхронный шаблон обмена сообщениями, в котором односторонние операции (операции, у которых свойство OperationContractAttribute.IsOneWay имеет значение true и нет согласованного ответа) могут отправляться в любом направлении клиентом или службой независимо от противоположной стороны. (Для этого используется шаблон обмена дуплексными сообщениями с односторонней рассылкой сообщений.) В этом случае контракт службы указывает односторонняя обмен сообщениями, который можно реализовать как асинхронные вызовы или реализации, или нет. Обычно, если контракт определяет односторонний обмен сообщениями, реализации бывают преимущественно асинхронными, поскольку после отправки сообщения приложение не дожидается ответа и продолжает выполнять другие операции.

Асинхронные клиенты на основе событий и контракты сообщений

Согласно правилам разработки для асинхронной модели на основе событий, если возвращается несколько значений, одно значение возвращается как свойство Result, а остальные возвращаются как свойства объекта EventArgs. Одним из результатов этого является то, что если клиент импортирует метаданные с использованием параметров асинхронных команд на основе событий и операция возвращает более одного значения, объект EventArgs по умолчанию возвращает одно значение как свойство Result, а остальные являются свойствами объекта EventArgs.

Если требуется получать объект сообщения как свойство Result, чтобы возвращаемые значения были свойствами этого объекта, следует использовать параметр командной строки /messageContract. При этом формируется сигнатура, которая возвращает ответное сообщение как свойство Result объекта EventArgs. Все внутренние возвращаемые значения тогда будут свойствами объекта ответного сообщения.

См. также