Operações síncronas e assíncronas

Este tópico discute a implementação e a chamada de operações de serviço assíncronas.

Muitos aplicativos chamam métodos de forma assíncrona porque isso permite que o aplicativo continue fazendo trabalho útil enquanto a chamada de método é executada. Os serviços e clientes do Windows Communication Foundation (WCF) podem participar de chamadas de operação assíncronas em dois níveis distintos do aplicativo, que fornecem aos aplicativos WCF ainda mais flexibilidade para maximizar a taxa de transferência equilibrada em relação à interatividade.

Tipos de operações assíncronas

Todos os contratos de serviço no WCF, independentemente dos tipos de parâmetros e valores de retorno, usam atributos WCF para especificar um padrão de troca de mensagens específico entre cliente e serviço. O WCF roteia automaticamente as mensagens de entrada e saída para a operação de serviço apropriada ou código de cliente em execução.

O cliente possui apenas o contrato de serviço, que especifica o padrão de troca de mensagens para uma operação específica. Os clientes podem oferecer ao desenvolvedor qualquer modelo de programação que escolherem, desde que o padrão de troca de mensagens subjacente seja observado. Assim, os serviços também podem implementar operações de qualquer maneira, desde que o padrão de mensagem especificado seja observado.

A independência do contrato de serviço da implementação de serviço ou cliente permite as seguintes formas de execução assíncrona em aplicativos WCF:

  • Os clientes podem invocar operações de solicitação/resposta de forma assíncrona usando uma troca de mensagens síncrona.

  • Os serviços podem implementar uma operação de solicitação/resposta de forma assíncrona usando uma troca de mensagens síncrona.

  • As trocas de mensagens podem ser unidirecionais, independentemente da implementação do cliente ou serviço.

Cenários assíncronos sugeridos

Use uma abordagem assíncrona em uma implementação de operação de serviço se a implementação de serviço de operação fizer uma chamada de bloqueio, como fazer trabalho de E/S. Quando você estiver em uma implementação de operação assíncrona, tente chamar operações e métodos assíncronos para estender o caminho de chamada assíncrona na medida do possível. Por exemplo, chame um BeginOperationTwo() de dentro BeginOperationOne()do .

  • Use uma abordagem assíncrona em um cliente ou aplicativo chamador nos seguintes casos:

  • Se você estiver invocando operações de um aplicativo de camada intermediária. (Para obter mais informações sobre esses cenários, consulte Aplicativos cliente de camada intermediária.)

  • Se você estiver invocando operações em uma página ASP.NET, use páginas assíncronas.

  • Se você estiver invocando operações de qualquer aplicativo que seja de thread único, como Windows Forms ou Windows Presentation Foundation (WPF). Ao usar o modelo de chamada assíncrona baseada em evento, o evento de resultado é gerado no thread da interface do usuário, adicionando capacidade de resposta ao aplicativo sem exigir que você manipule vários threads por conta própria.

  • Em geral, se você tiver uma escolha entre uma chamada síncrona e assíncrona, escolha a chamada assíncrona.

Implementando uma operação de serviço assíncrona

As operações assíncronas podem ser implementadas usando um dos três métodos a seguir:

  1. O padrão assíncrono baseado em tarefas

  2. O padrão assíncrono baseado em evento

  3. O padrão assíncrono IAsyncResult

Padrão assíncrono baseado em tarefas

O padrão assíncrono baseado em tarefas é a maneira preferida de implementar operações assíncronas porque é o mais fácil e direto. Para usar esse método, basta implementar sua operação de serviço e especificar um tipo de retorno de Tarefa<T>, onde T é o tipo retornado pela operação lógica. Por exemplo:

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

A operação SampleMethodTaskAsync retorna a cadeia de caracteres> Task<porque a operação lógica retorna uma cadeia de caracteres. Para obter mais informações sobre o padrão assíncrono baseado em tarefas, consulte O padrão assíncrono baseado em tarefas.

Aviso

Ao usar o padrão assíncrono baseado em tarefas, uma T:System.AggregateException pode ser lançada se ocorrer uma exceção enquanto aguarda a conclusão da operação. Esta exceção pode ocorrer no cliente ou serviços

Padrão assíncrono baseado em eventos

Um serviço que ofereça suporte ao padrão assíncrono baseado em evento terá uma ou mais operações chamadas MethodNameAsync. Esses métodos podem espelhar versões síncronas, que executam a mesma operação no thread atual. A classe também pode ter um evento MethodNameCompleted e pode ter um método MethodNameAsyncCancel (ou simplesmente CancelAsync). Um cliente que deseja chamar a operação definirá um manipulador de eventos a ser chamado quando a operação for concluída,

O trecho de código a seguir ilustra como declarar operações assíncronas usando o padrão assíncrono baseado em evento.

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.
}

Para obter mais informações sobre o padrão assíncrono baseado em evento, consulte O padrão assíncrono baseado em evento.

Padrão assíncrono IAsyncResult

Uma operação de serviço pode ser implementada de forma assíncrona usando o padrão de programação assíncrona do .NET Framework e marcando o <Begin> método com a AsyncPattern propriedade definida como true. Nesse caso, a operação assíncrona é exposta em metadados da mesma forma que uma operação síncrona: ela é exposta como uma única operação com uma mensagem de solicitação e uma mensagem de resposta correlacionada. Os modelos de programação de clientes têm então uma escolha. Eles podem representar esse padrão como uma operação síncrona ou assíncrona, desde que, quando o serviço é invocado, ocorra uma troca de mensagens de solicitação-resposta.

Em geral, com a natureza assíncrona dos sistemas, você não deve depender dos threads. A maneira mais confiável de passar dados para vários estágios de processamento de despacho de operação é usar extensões.

Para obter um exemplo, consulte Como implementar uma operação de serviço assíncrona.

Para definir uma operação X de contrato que é executada de forma assíncrona, independentemente de como ela é chamada no aplicativo cliente:

  • Defina dois métodos usando o padrão BeginOperation e EndOperation.

  • O BeginOperation método inclui in parâmetros para ref a operação e retorna um IAsyncResult tipo.

  • O EndOperation método inclui um IAsyncResult parâmetro, bem como os out parâmetros e ref e retorna o tipo de retorno de operações.

Por exemplo, consulte o seguinte método.

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

Para criar uma operação assíncrona, os dois métodos seriam:

[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

Nota

O OperationContractAttribute atributo é aplicado somente ao BeginDoWork método. O contrato resultante tem uma operação WSDL chamada DoWork.

Invocações assíncronas do lado do cliente

Um aplicativo cliente WCF pode usar qualquer um dos três modelos de chamada assíncrona descritos anteriormente

Ao usar o modelo baseado em tarefas, basta chamar a operação usando a palavra-chave await, conforme mostrado no trecho de código a seguir.

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

Usar o padrão assíncrono baseado em evento requer apenas a adição de um manipulador de eventos para receber uma notificação da resposta -- e o evento resultante é gerado no thread da interface do usuário automaticamente. Para usar essa abordagem, especifique as opções de comando /async e /tcv:Version35 com a ServiceModel Metadata Utility Tool (Svcutil.exe), como no exemplo a seguir.

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

Quando isso é feito, Svcutil.exe gera uma classe de cliente WCF com a infraestrutura de eventos que permite que o aplicativo de chamada implemente e atribua um manipulador de eventos para receber a resposta e tomar a ação apropriada. Para obter um exemplo completo, consulte Como chamar operações de serviço de forma assíncrona.

O modelo assíncrono baseado em evento, no entanto, só está disponível no .NET Framework 3.5. Além disso, ele não é suportado mesmo no .NET Framework 3.5 quando um canal de cliente WCF é criado usando um System.ServiceModel.ChannelFactory<TChannel>arquivo . Com objetos de canal de cliente WCF, você deve usar System.IAsyncResult objetos para invocar suas operações de forma assíncrona. Para usar essa abordagem, especifique a opção de comando /async com a ServiceModel Metadata Utility Tool (Svcutil.exe), como no exemplo a seguir.

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

Isso gera um contrato de serviço no qual cada operação é modelada como um <Begin> método com a AsyncPattern propriedade definida como true e um método correspondente <End> . Para obter um exemplo completo usando um ChannelFactory<TChannel>, consulte Como chamar operações de forma assíncrona usando uma fábrica de canais.

Em ambos os casos, os aplicativos podem invocar uma operação de forma assíncrona, mesmo que o serviço seja implementado de forma síncrona, da mesma forma que um aplicativo pode usar o mesmo padrão para invocar de forma assíncrona um método síncrono local. A forma como a operação é implementada não é significativa para o cliente; Quando a mensagem de resposta chega, seu conteúdo é enviado para o método assíncrono <End> do cliente e o cliente recupera as informações.

Padrões de troca de mensagens unidirecionais

Você também pode criar um padrão assíncrono de troca de mensagens no qual operações unidirecionais (operações para as quais o OperationContractAttribute.IsOneWay is true não tem resposta correlacionada) podem ser enviadas em qualquer direção pelo cliente ou serviço independentemente do outro lado. (Isso usa o padrão de troca de mensagens duplex com mensagens unidirecionais.) Nesse caso, o contrato de serviço especifica uma troca de mensagens unidirecional que qualquer um dos lados pode implementar como chamadas ou implementações assíncronas, ou não, conforme apropriado. Geralmente, quando o contrato é uma troca de mensagens unidirecionais, as implementações podem ser em grande parte assíncronas, porque uma vez que uma mensagem é enviada, o aplicativo não espera por uma resposta e pode continuar fazendo outro trabalho.

Clientes assíncronos baseados em eventos e contratos de mensagem

As diretrizes de design para o modelo assíncrono baseado em evento afirmam que, se mais de um valor for retornado, um valor será retornado como a Result propriedade e os outros serão retornados como propriedades no EventArgs objeto. Um resultado disso é que, se um cliente importar metadados usando as opções de comando assíncrono baseadas em evento e a operação retornar mais de um valor, o objeto padrão EventArgs retornará um valor como a Result propriedade e o restante serão propriedades do EventArgs objeto.

Se desejar receber o objeto message como a Result propriedade e ter os valores retornados como propriedades nesse objeto, use a opção de comando /messageContract . Isso gera uma assinatura que retorna a mensagem de resposta como a Result propriedade no EventArgs objeto. Todos os valores de retorno internos são, então, propriedades do objeto de mensagem de resposta.

Consulte também