Este artigo foi traduzido por máquina.

Cutting Edge

Revisitando as páginas assíncronas do ASP.NET

Dino Esposito

ASP.NET sempre tem suporte para manipuladores HTTP síncronos e assíncronos. Agora, o ASP.NET 2.0 tem novos recursos para tornar mais fácil e mais rápido para os desenvolvedores a criar páginas assíncronas. Especialmente para aplicativos baseados em servidor, operações assíncronas são fundamentais para habilitar a escalabilidade. Se que você precisa expandir o seu aplicativo da Web existente, o primeiro aspecto a considerar é quanto assincronismo podem adicionar às páginas.

Nesse aspecto, ASP.NET se comporta como qualquer outro aplicativo de servidor que executa algum trabalho plano de fundo em nome de vários clientes. Cada solicitação de entrada é atribuída a um segmento pertence ASP.NET pegou do pool de threads do ASP.NET. O thread permanece bloqueado até que a operação foi finalizado e alguns resposta foi gerada para o cliente. Quanto tempo o thread deve esperar? O ambiente de runtime do ASP.NET pode ser configurado para definir um tempo limite personalizado (90 segundos é o padrão), mas é mais importante para impedir que o thread seja bloqueado.

Quando você lida com operações potencialmente demoradas, o tempo limite no máximo, somente garante que após um determinado número de segundos, o thread será liberado e retornado ao pool. Em vez disso, o que você deseja é manter o thread seja bloqueado por um longo tempo. Idealmente, você deseja que o thread para iniciar uma solicitação e produzi-lo a algum outro segmento que não sejam do ASP.NET. O mesmo thread ou um outro do pool de ASP.NET será selecionado de novamente após a conclusão da operação para enviar a resposta ao cliente. Esse paradigma é conhecida como páginas assíncronas do ASP.NET.

Quando se trata de operações assíncronas, você deve distinguir entre as páginas que são assíncronas com relação ao usuário e páginas que são assíncronas em relação ao tempo de execução do ASP.NET. Para páginas assíncronas com relação ao usuário, a abordagem somente viável é uma operação de AJAX. No entanto, usando AJAX para realizar uma operação potencialmente lenta diminui o impacto sobre o usuário final mas não exibir nenhum alívio para o tempo de execução 
ASP.NET.

Páginas assíncronas e o tempo de execução do ASP.NET

Quanto maior o thread trava para a solicitação, mais um thread é subtraído do pool ASP.NET para atender a novas solicitações de entrada. Quando nenhum segmento está disponível para atender a novas solicitações, as solicitações são enfileiradas. Isso pode causar atrasos e degradação do desempenho geral.

No ASP.NET, manipuladores HTTP são síncronos por padrão. Manipuladores HTTP assíncronos devem ser explicitamente projetados e implementados pela aplicação de um pouco diferentes interfaces. Um manipulador síncrono difere de um manipulador assíncrono em um aspecto importante: em vez do método ProcessRequest síncrono, um manipulador assíncrono usa os métodos listados abaixo, que são parte da interface do IHttpAsyncHandler:

IAsyncResult BeginProcessRequest(

     HttpContext context, 

     AsyncCallback cb, 

     object extraData);


void EndProcessRequest(
     

     IAsyncResult result);

BeginProcessRequest contém a operação a ser executada para a solicitação. Esse código deve ser projetado para iniciar a operação em um thread secundário e retornar imediatamente. EndProcessRequest contém o código para concluir a solicitação que anteriormente foi iniciada.

Como você pode ver, uma solicitação HTTP assíncrona é dividida em duas partes — antes e depois “ ponto assíncrono ” — o ponto do ciclo de vida da solicitação onde altera o segmento que possui a solicitação. Quando o ponto assíncrono é alcançado, o segmento original ASP.NET produz controle para outro thread. Esta operação potencialmente demorada ocorre entre as duas partes da solicitação ASP.NET. Cada parte da solicitação assíncrona executa independente das outras e com nenhuma afinidade que diz respeito ao thread. Em outras palavras, não há nenhuma garantia que o mesmo thread cuidará do duas partes da solicitação. O resultado é que nenhum thread é bloqueado durante a operação.

Neste ponto, a pergunta óbvia é: segmento que realmente se encarrega de operação “ demorada ”? O ASP.NET usa portas de conclusão E/s internamente para controlar a terminação de uma solicitação. Quando o ponto assíncrono é alcançado, o ASP.NET vincula a solicitação pendente a uma porta de conclusão E/s e registrar um retorno de chamada para obter uma notificação quando a solicitação foi finalizado. O sistema operacional usará um dos seus próprios threads dedicados para monitorar a finalização da operação, liberando, portanto, o thread ASP.NET tenha de esperar totalmente ocioso. Quando a operação termina, o sistema operacional coloca uma mensagem na fila de conclusão, que aciona o retorno de chamada do ASP.NET que, em seguida, selecionar um dos seus próprios threads para continuar a solicitação. Como mencionado, portas de conclusão de E/s são um recurso do sistema operacional.

A natureza real das páginas assíncronas

No ASP.NET, páginas assíncronas são normalmente associadas com a idéia de melhorar o desempenho de uma determinada página encarregados de realizar uma operação potencialmente demorada. No entanto, devem-se observar alguns pontos adicionais. Da perspectiva do usuário, as solicitações síncronas e assíncronas quase a mesma aparência. Se a operação solicitada deve demorar, digamos, 30 segundos para concluir, o usuário aguardará pelo menos 30 segundos para voltar a nova página. Isso acontece independentemente da implementação síncrona ou assíncrona da página. Além disso, a Don se muito surpreso se uma página assíncrona acaba demorando um pouco mais para concluir uma solicitação única. Então, qual é a vantagem de páginas assíncronas?

Escalabilidade não é muito semelhante a desempenho. Ou, pelo menos, a escalabilidade é sobre o desempenho, mas em um nível diferente — o aplicativo inteiro em vez de uma única solicitação. O benefício que páginas assíncronas trazem para a tabela é muito menos trabalho para threads no pool de ASP.NET. Isso não faz solicitações de longas execução mais rápida, mas ajuda o sistema atender as solicitações não longas como de costume — ou seja, sem atrasos especial resultantes da contínua lento solicitações.

Solicitações assíncronas aproveitam os manipuladores HTTP assíncronos sempre foram um recurso da plataforma ASP.NET. No entanto, o ASP.NET Web Forms e ASP.NET MVC fornecem seus próprios recursos para simplificar para os desenvolvedores implementar ações assíncronas. No restante deste artigo, discutirei as operações assíncronas no ASP.NET MVC 2.

Ações do controlador assíncrono

No ASP.NET MVC 1.0, qualquer ação de controlador só pode executar sincronizadamente. No entanto, uma nova classe AsyncController foi adicionada à biblioteca MVC futura. Após um período experimental, o API assíncrono para controladores oficialmente foi adicionado à estrutura MVC do ASP.NET e é totalmente documentados como da versão 2 da estrutura MVC do ASP.NET e disponível. (A sintaxe e os recursos discutidos neste artigo consulte ASP.NET MVC 2 RC.)  Se você executar um pouco com a classe AsyncController na biblioteca MVC futura, você irá notar algumas alterações e a API é mais simples e mais limpo.

A finalidade de AsyncController é garantir que todos os métodos expostos ação executar de forma assíncrona sem alterar a abordagem geral para a programação caracteriza o ASP.NET MVC framework. O diagrama na Figura 1 mostra a seqüência de etapas por trás do processamento de uma ação assíncrona.


Figura 1 Mecânica de um método de ação assíncronas no ASP.NET MVC

O ponto assíncrono é colocado entre a execução e executavam eventos. Quando o chamador de ação notifica que ele é sobre como executar a ação, o segmento contratado ainda é o segmento original do ASP.NET que pegou a solicitação da fila de servidor Web. Neste ponto, a ação é executada. No final, quando estiver pronto para notificar o evento a ação executada, o chamador ação possivelmente outro thread do ASP.NET é cuidar da solicitação. A Figura 2 ilustra esse cenário.


Figura 2 Alternando para uma chamada de método assíncrono ação de thread

Antes de discutir os detalhes de como criar e depurar métodos assíncronos, outro ponto fundamental de operações assíncronas do ASP.NET deve ser feito claro: nem todas as ações são bons candidatos para se tornar as operações assíncronas.

O destino real das operações assíncronas

Somente operações vinculadas a O são bons candidatos para tornar-se métodos de ação assíncrona em uma classe de controlador assíncrono. Uma operação vinculada a O é uma operação que não depende da CPU local para a conclusão. Quando uma operação vinculada a O está ativa, a CPU apenas aguarda dados a ser processada (isto é, baixados) de armazenamento externo (um banco de dados ou um serviço remoto). Operações vinculadas a O estão em comparação com operações vinculadas à CPU, onde a conclusão de uma tarefa depende da atividade da CPU.

Um exemplo típico de uma operação vinculada a O é chamada de um serviço remoto. Nesse caso, os métodos de ação disparar a solicitação e apenas aguarde qualquer resposta sejam baixados. O trabalho real é que está sendo feito remotamente por outra máquina e outra CPU. Assim, o segmento do ASP.NET está preso em espera e de ociosidade. Liberação de que o thread ocioso de imposto de esperar para atender a outras solicitações de entrada é o ganho de desempenho você pode obter com a implementação assíncrona de ações ou páginas.

Acontece que nem todas as operações demoradas fornecerão um benefício concreto se implementada de modo assíncrono. Um cálculo de memória longo significativamente não se beneficiar da implementação assíncrona. Até mesmo, ele foi executado um pouco mais lenta, porque a mesma CPU está servindo de solicitação ASP.NET e o cálculo. Além disso, talvez ainda seja necessário um segmento ASP.NET fisicamente cuidar do cálculo. Há poucos benefícios, se houver, usando a implementação assíncrona para operações de âmbito da CPU. Por outro lado, se a recursos remotos estão envolvidos, até mesmo várias 
resources, usando métodos assíncronos realmente pode aumentar o desempenho do aplicativo, se não o desempenho da solicitação individual.

Voltarei a este ponto daqui a pouco com um exemplo. Por enquanto, let’s enfocam a sintaxe necessária para definir e executar ações assíncronas no ASP.NET MVC.

Reconhecendo Async rotas

Em que a forma é uma rota assíncrono diferente de uma rota síncrona? Na versão futura do MVC, você foi solicitado para usar diferentes métodos para registrar rotas síncronas e assíncronas. Eis a maneira antiga para registrar uma rota assíncrono:

routes.MapAsyncRoute(

    "Default",

    "{controller}/{action}/{id}",

    new { controller = "Home", action = "Index", id = "" }

);

Você precisava usar o método de extensão MapAsyncRoute em vez do MapRoute padrão, você deve ter usado para métodos síncronos clássicos. No ASP.NET MVC 2 RC, no entanto, essa distinção foi removida. Agora você tem apenas uma forma para registrar suas rotas — o método MapRoute — independentemente de como a ação, em seguida, ser executada.

URL da solicitação é processada, portanto, como de costume, e o nome da classe de controlador para usar é descoberto. Na verdade, é necessário, que um método assíncrono é definido em uma classe de controlador que deriva da nova classe AsyncController, ilustrada aqui:

public class TestController : AsyncController

{

  ...

}

Se a classe controladora é herdada de AsyncController, a convenção para mapear nomes de ação para métodos é um pouco diferente. Uma classe AsyncController pode servir solicitações síncronas e assíncronas. Como resultado, a convenção usada pode reconhecer tanto um método de execução e um método RunAsync, conforme mostrado aqui:

public class TestController : AsyncController

{

  public ActionResult Run(int id) 

  {

     ...

  }

  public void RunAsync(int id) 

  {

     ...

  }

}

Se fizer isso, no entanto, uma exceção será gerada (consulte Figura 3).

Uma ação assíncrona é identificada pelo nome e o padrão esperado é xxxAsync, onde xxx indica o nome padrão da ação executar. Obviamente, se existe outro método chamado xxx e ele não é disambiguated usando atributos, em seguida, uma exceção é lançada como em Figura 3.


Figura 3 Referências ambíguas no nome da ação de

A palavra Async é considerada um sufixo. A URL para invocar o método RunAsync conterá somente o prefixo executar. Por exemplo, a seguinte URL invocará o método RunAsync, passando um valor 5 como um parâmetro de rota:

http://myserver/demo/run/5

Se isso será resolvido como uma ação síncrona ou assíncrona, os métodos depende você tem a classe AsyncController. O método xxxAsync, no entanto, identifica somente o disparador da operação. O finalizador da solicitação é outro método na classe de controlador chamada xxxCompleted:

public ActionResult RunCompleted(DataContainer data)

{

    ...

}

Observe a assinatura diferente de dois métodos Definir ação assíncrona. O disparador deve ser um método void. Se você defini-la para qualquer valor de retorno, o valor de retorno simplesmente será ignorado. Os parâmetros de entrada do método xxxAsync estará sujeita a modelo de vinculação como de costume. O método do finalizador retorna um objeto ActionResult normalmente e recebe um objeto personalizado que contém os dados que possui espera de processo e passar para o objeto de exibição. Um protocolo especial é necessário para os valores calculados por disparador os parâmetros declarados, o finalizador de correspondência.

A classe AsyncController

A classe de controlador AsyncController herda do controlador e implementa um monte de novas interfaces conforme mostrado aqui:

public abstract class AsyncController : Controller, 

                IAsyncManagerContainer, 

IAsyncController, IController

O aspecto mais distinto de um controlador assíncrono é o objeto de chamador ação especial que é empregado nos bastidores para executar operações. As necessidades de chamador encerrado de um contador para controlar o número de operações individuais que compõem a ação e que devem ser sincronizados antes que a ação geral pode ser declarada. Figura 4 fornece uma implementação de exemplo para uma ação assíncrona.

Figura 4 Um método de ação assíncrono simples

public void RunAsync(int id) 

{

    AsyncManager.OutstandingOperations.Increment();



    var d = new DataContainer();

     ...

            

    // Do some remote work (i.e., invoking a service)

     ...



    // Terminate operations

    AsyncManager.Parameters["data"] = d;

    AsyncManager.OutstandingOperations.Decrement();

}

public ActionResult RunCompleted(DataContainer data)

{

   ...

}

O membro OutstandingOperations na classe AsyncManager fornece um contêiner que mantém uma contagem de operações assíncronas pendentes. Ele é uma instância da classe auxiliar OperationCounter e fornece uma API ad hoc para incrementar e decrementar. O método Increment não está limitado a incrementos unários, conforme mostrado aqui:

AsyncManager.OutstandingOperations.Increment(2);

service1.GetData(...);

AsyncManager.OutstandingOperations.Decrement();

service2.GetData(...);

AsyncManager.OutstandingOperations.Decrement();

O dicionário de parâmetros AsyncManager é usado para valores de grupo sejam passados como argumentos para o método do finalizador da chamada assíncrona. O dicionário de parâmetros deve conter uma entrada para cada parâmetro a ser passada para o finalizador — o método xxxCompleted no exemplo anterior. Se não for possível encontrar uma correspondência entre entradas nos nomes de parâmetro e de dicionário, um valor padrão é adotado para o parâmetro – nulo para tipos de referência. Nenhuma exceção é lançada, a menos que uma tentativa de acessar um objeto nulo. O método xxxCompleted recebe parâmetros de qualquer tipo com suporte e os usa para preencher a coleção ViewData ou qualquer objeto de alta segurança de tipos reconhecidos pelo modo de exibição. O método xxxCompleted é responsável por retornar um objeto ActionResult.

Um bom ajuste ou não?

Quebra automática de, solicitações síncronas são um recurso necessário no ASP.NET e, na verdade, manipuladores HTTP assíncronos tem sido suportados desde o ASP.NET 1.0.

ASP.NET Web Forms e ASP.NET MVC oferecem ferramentas de nível mais alto para operações assíncronas código, cada um seu próprio modelo de aplicativo — no ASP.NET MVC, você tem controladores assíncronos e nos Web Forms você depende de páginas assíncronas. 

O aspecto importante de ações assíncronas, porém, é decidir se uma determinada tarefa é uma boa opção para uma implementação assíncrona. Métodos assíncronos somente devem ser criados em torno de operações vinculadas a O. E, finalmente, tenha em mente que assíncrono métodos não serão executados mais rapidamente se, mas permitirá que outras solicitações para serem executados mais rapidamente.

Dino Esposito  é autor do futuro “Programming ASP.NET MVC” da Microsoft Press e foi co-autor “.NET da Microsoft: Arquitetando aplicativos para a empresa” (Microsoft Press, 2008). Residente na Itália, Esposito é um palestrante freqüente em eventos do setor no mundo inteiro. Você pode ingressar em seu blog em weblogs.ASP.net/despos.

Graças ao especialista técnico seguir para revisar este artigo: Stefan Schackow