Cultura padrão para AppDomain

by Israel Aece 22. September 2013 16:43

O idioma configurado na minha máquina (através do Painel de Controle) é o pt-BR. Ao rodar uma aplicação .NET, por padrão, a mesma é configurada com a cultura que está definida lá, e sendo assim, todas as threads que rodam na aplicação seguirão a mesma cultura. O código abaixo ilustra o código de exemplo:

static void Main(string[] args)
{
    var data = DateTime.Now;
    var valor = 1982.81M;

    ExibirNaMesmaThread(data, valor);
    ExibirEmOutraThread(data, valor);
}

static void ExibirNaMesmaThread(DateTime data, decimal valor)
{
    Console.WriteLine(data);
    Console.WriteLine("{0:C2}", valor);
}

static async void ExibirEmOutraThread(DateTime data, decimal valor)
{
    await Task.Delay(1000);

    Console.WriteLine(data);
    Console.WriteLine("{0:C2}", valor);
}

O resultado ao rodar este código será:

22/09/2013 16:33:34
R$ 1.982,81

22/09/2013 16:33:34
R$ 1.982,81

A data e o valor decimal estão no formato brasileiro, assim como era de se esperar. Em aplicações onde queremos determinar e/ou fixar a cultura padrão, recorremos à propriedade CurrentCulture da classe Thread para isso, e geralmente fazemos a configuração na inicialização da aplicação. Só que essa configuração somente é válida quando estamos em uma aplicação que trabalha apenas com uma única thread. Se desejarmos configurar a cultura explicitamente, fazemos o seguinte:

var cultura = new CultureInfo("en-US");

Thread.CurrentThread.CurrentCulture = cultura;
Thread.CurrentThread.CurrentUICulture = cultura;

Só que ao rodar a aplicação, para nossa surpresa, teremos:

9/22/2013 4:37:58 PM
$1,982.81

22/09/2013 16:37:58
R$ 1.982,81

A primeira exibição mostra os valores no padrão americano, pois está rodando sobre na mesma thread em que configuramos a cultura. Já o método que roda assincronamente e, consequentemente, em outra thread, continua exibindo os valores no formato brasileiro, pois a configuração que fizemos afetou somente a thread corrente, e Task que foi criada pelo .NET para executar o método ExibirEmOutraThread continua utilizando no formato brasileiro.

A partir do .NET Framework 4.5, a classe CultureInfo passa a ter duas novas propriedades estáticas chamadas: DefaultThreadCurrentCulture e DefaultThreadCurrentUICulture, que determinam a cultura padrão para todas as threads que rodam dentro daquele AppDomain. Se optarmos por ela ao invés daquelas propriedades expostas pela classe Thread, teremos ambos os valores formatados igualmente para todos os métodos dentro da aplicação, independentemente de qual thread eles estejam rodando.

var cultura = new CultureInfo("en-US");

CultureInfo.DefaultThreadCurrentCulture = cultura;
CultureInfo.DefaultThreadCurrentUICulture = cultura;

9/22/2013 4:43:19 PM
$1,982.81

9/22/2013 4:43:19 PM
$1,982.81

Tags: ,

.NET Framework | Async

Utilizando o DataReader Assincronamente

by Israel Aece 22. April 2012 21:58

Na versão 2.0 do .NET Framework, a Microsoft incluiu uma série de novas funcionalidades em sua API de acesso a dados, o ADO.NET. Entre elas, podemos destacar o código genérico, MARS, Bulk-Copy e execução assíncrona de comandos e consultas.

O que tínhamos disponível naquela época é a implementação através do modelo assíncrono do .NET, que era implementado utilizando um par de métodos BeginXXX/EndXXX. Sendo assim, o método ExecuteReader passou a ter os métodos BeginExecuteReader e EndExecuteReader, enquanto o método ExecuteNonQuery, ganhou os métodos BeginExecuteNonQuery e EndExecuteNonQuery.

Da mesma forma, para ficar alinhado a nova forma de se trabalhar assincronamente nas linguagens, a execução assíncrona de comandos e consultas no ADO.NET 4.5 sofreu algumas mudanças, para seguir o modelo baseado em Tasks. Além das mudanças em nível das interfaces das classes, um detalhe importante é que não é mais necessário definir o flag Asynchronous Processing para True no arquivo de configuração, algo que sempre era descoberto somente depois que a aplicação estava em execução.

Para iniciar, a classe que representa a conexão (SqlConnection/DbConnection) fornecem a versão assíncrona do método Open, que é o OpenAsync. Este método retorna uma Task, o que a torna "aguardável", e com isso, podemos utilizar a keyword await para que a abertura possa ser realizada de forma assíncrona. Abaixo o código ilustra o uso deste método:

private async static Task Executar()
{
    using (var conn = new SqlConnection("..."))
    {
        await conn.OpenAsync();

        //...
    }
}

Como já era de se esperar, os mesmos métodos fornecidos na versão 2.0 do ADO.NET para processamento assíncrono, ganharam na versão baseada em Tasks na versão 4.5. No caso do ExecuteReader, temos o ExecuteReaderAsync. Já para o método ExecuteNonQuery, temos o ExecuteNonQueryAsync e, finalmente, para o ExecuteScalar, existe o ExecuteScalarAsync.

Todos estes métodos tratam-se da nova versão assíncrona, que basicamente retornam um objeto do tipo Task, que representa a tarefa que está sendo executada assincronamente. E, qualquer exceção que eventualmente ocorra dentro do processo assíncrono, ela será retornada/acessada através da Task que foi retornada pelo método. Abaixo temos um exemplo de como ler os dados através de DataReader, utilizando este novo modelo assíncrono:

private async static Task Executar()
{
    using (var conn = new SqlConnection("..."))
    {
        await conn.OpenAsync();

        using (var cmd = new SqlCommand("SELECT * FROM Cliente", conn))
            using (var dr = await cmd.ExecuteReaderAsync())
                while (await dr.ReadAsync())
                    if (!await dr.IsDBNullAsync(1))
                        Console.WriteLine(dr.GetString(1));
    }
}

Acima utilizamos o método ExecuteReaderAsync, mas ao percorrer o result-set retornado, utilizamos o - também novo - método ReaderAsync, que é a versão assíncrona, também baseada em Task,  do método Read do DataReader. Esse método em conjunto os métodos NextResultAsync, IsDBNullAsync e GetFieldValueAsync<T>, fornecem um controle muito mais refinado aos dados que estão sendo extraídos, pois quando olhamos um pouco mais de perto os internals de cada um deles, percebemos que a versão síncrona pode custar caro, prejudicando assim a escalabilidade.

Além disso, todos os métodos que vimos até aqui, possuem um segundo overload que suporta o cancelamento da tarefa custosa que está sendo executada. Para controlar o cancelamento, eles fazem uso da estrutura CancellationToken, e que podemos criar e informar ao invocar o método. Com uma pequena mudança na assinatura do método de exemplo que criamos acima (Executar), ele passará a receber o token que controla e propaga a notificação de cancelamento. Uma vez que o mesmo é repassado às tarefas que são executadas internamente, periodicamente o token é consultado para ver se o cancelamento foi ou não solicitado. A mudança é ligeira:

private async static Task Executar(CancellationToken ct)
{
    using (var conn = new SqlConnection("..."))
    {
        await conn.OpenAsync(ct);

        using (var cmd = new SqlCommand("SELECT * FROM Cliente", conn))
            using (var dr = await cmd.ExecuteReaderAsync(ct))
                while (await dr.ReadAsync(ct))
                    if (!await dr.IsDBNullAsync(1, ct))
                        Console.WriteLine(dr.GetString(1));
    }
}

Como percebemos, para preparar o método para permitir o cancelamento, é receber no parâmetro um CancellationToken, e propagá-lo para os métodos internos. Abaixo, estamos consumindo o método Executar que criamos, só que agora estamos passando um token que que será cancelado em dois segundos. Se depois deste tempo o método não concluir, uma exceção será disparada, informando que a tarefa foi cancelada.

var cts = new CancellationTokenSource();

try
{
    cts.CancelAfter(TimeSpan.FromSeconds(2));
    Executar(cts.Token).Wait();
}
catch (Exception ex)
{
    //trata exceção
}

Tags: , ,

Async | Data

Contratos Assíncronos no WCF 4.5

by Israel Aece 8. April 2012 19:33

Há algum tempo eu mencionei aqui como criar um contrato assíncrono para um serviço WCF. A finalidade deste tipo de contrato é permitir a sua implementação de forma, também, assíncrona, para que possamos tirar um hor proveito dos recursos (leia-se threads), do servidor onde o serviço estará sendo hospedado.

Como o formato segue o padrão estabelecido pelo .NET Framework, somos obrigados a construir o contrato nos mesmos moldes do padrão definido por ele, ou seja, um par de métodos Begin/End. Além disso, ainda temos a necessidade de uma eventual implementação da interface IAsyncResult, callbacks, etc., ou seja, com toda a complexidade que estamos acostumados quando precisamos lidar com programação assíncrona no .NET.

As linguagens (C# e VB.NET) redefiniram a forma como escrevemos código assíncrono, e para tirar proveito disso, o WCF sofreu algumas mudanças internas, e agora a criação e implementação de contratos assíncronos são bem mais fáceis de serem implementados do que anteriormente.

Apesar das mudanças serem internas, para que seja possível esse tipo de utilização, algumas regras são exigidas ao (re)escrever o contrato. É necessário que a operação retorne um objeto do tipo Task (quando a mesma não retorna nenhum resultado) ou Task<T> (quando a mesma deve retornar um resultado (T refere-se ao tipo de resultado)). Abaixo temos a interface que define o contrato do serviço:

[ServiceContract]
public interface IContrato
{
    [OperationContract]
    Task<string> Ping(string value);
}

Ao implementar esse contrato na classe que representará o serviço, entre em cena as keywords do C# que faz toda a mágica para executar de forma assíncrona: async e await, quais já falamos aqui, e que em conjunto com os ajustes internos que a Microsoft fez no WCF (a crição de um operation invoker chamado TaskMethodInvoker), faz com que a operação seja executada sem qualquer grande impacto ao desenvolvedor. Abaixo temos um exemplo de implementação do contrato acima criado:

public class Servico : IContrato
{
    public async Task<string> Ping(string value)
    {
        return await Task.Factory.StartNew(() => value + " ping");
    }
}

Em termos que hosting, nada é necessário. Por fim, como já mencionado, em tempo de execução, teremos uma melhor reusabilidade das threads, o que nos permite executar algum recurso custoso, como por exemplo, chamado à bancos de dados, outros serviços, sem que a thread fique bloqueada esperando pelo resultado que não depende das capacidades do processamento local.

Já o consumo por parte de uma aplicação também .NET, temos uma ligeira mudança. Trata-se de uma nova opção chamada "Generate task-based operations". Ao efetuar a referência para um serviço e se esta opção estiver selecionada, fará com que a versão assíncrona das operações do proxy, sejam criadas utilizando o modelo baseado em tasks. Enquanto a outra opção, fará com que ele crie no modelo tradicional, ou seja, baseado em eventos XXXCompleted para cada uma das operações expostas pelo serviço. A imagem abaixo ilustra esta nova opção:

Tags: , ,

Async | WCF

Reportando o Progresso de Tarefas Assíncronas

by Israel Aece 4. March 2011 12:05

Uma das necessidades que circundam o processamento assíncrono de alguma tarefa é como reportar o progresso da mesma, para dar um feedback, na maioria das vezes para um usuário, do estágio em que ela se encontra. Na maioria das vezes, isso está associado com interfaces gráficas (GUI), que por sua vez, podem trabalhar utilizando o modelo síncrono ou assíncrono.

Optamos pelo modelo assíncrono justamente permitir que o usuário possa realizar outras tarefas, enquanto aquela que é custosa seja processada por uma segunda thread. É neste ponto que temos a necessidade de saber como anda o progresso do processamento, repassando ao usuário onde se encontra a execução da tarefa.

A Microsoft trabalha em um projeto para tornar a programação assíncrona tão simples quanto a programação síncrona. Este projeto introduz duas keywords, chamadas async e await, que fazem todo o trabalho para "recriar" o código que escrevemos, montando-o da forma necessária para executá-lo de forma assíncrona, sem a necessidade de precisarmos saber como isso é realizado.

Para reportar o progresso temos dois tipos que foram introduzidos: a interface IProgress<T> e a classe EventProgress<T>. A interface genérica IProgress<T> define a estrutura de reporte de progresso, e que não fornece nada mais do que um método chamado Report, que recebe um parâmetro do tipo T, que pode ser qualquer tipo, desde um simples inteiro até uma classe que reporte informações mais detalhes do progresso da tarefa. Já a classe EventProgress<T> é uma implementação desta interface que acabamos de ver, fornecendo a opção de sermos notificados através de um evento. A imagem abaixo mostra a estrutura desses tipos:

A instância da classe EventProgress<T> pode ser criada de duas formas: através da instância diretamente ou através do método estático From. Mas a segunda opção, na versão atual (ainda em CTP), está com um problema, disparando uma exceção de forma indevida. Para exemplificar, temos um formulário Windows Forms, que ao clicar em um botão do mesmo, executaremos uma tarefa custosa, e que deverá reportar ao usuário o progresso da mesmo, utilizando esses tipos que vimos agora, em conjunto com as keywords async/await.

O primeiro passo é instanciar a classe EventProgress<T>, onde o tipo genérico T, será definido como um número inteiro, indicando a porcentagem do progresso, que utilizaremos para incrementar um controle ProgressBar. Depois da classe instanciada, vamos nos vincular ao evento ProgressChanged, que será disparado todo o momento em que o processo custoso desejar reportar o andamento do progresso do mesmo, e como já era de se esperar, um número inteiro será passado como parâmetro através da instância da classe EventArgs<T>, que possui um parâmetro genérico do mesmo tipo em que foi informado na construção da classe EventProgress<T>, acessível através da propriedade Value. O código abaixo ilustra essa parte inicial:

private EventProgress<int> report = new EventProgress<int>();

public Form1()
{
    report.ProgressChanged += (sender, args) => this.progressBar1.Value = args.Value;
}

Com a classe que servirá como forma de reportar o progresso, vamos agora criar o código que simula uma operação custosa, e invocá-lo a partir do evento Click de um botão qualquer. Note que o método ExecutarTarefaCustosa já está decorado com a keyword async. Além disso, temos a keyword await definida no interior do mesmo método, que determina que aquilo que vem na frente dela, é considerado como o callback.

private void button1_Click(object sender, EventArgs e)
{
    ExecutarTarefaCustosa();
}

private async void ExecutarTarefaCustosa()
{
    for (int i = 1; i <= 10; i++)
    {
        await TaskEx.Delay(TimeSpan.FromSeconds(2)); //Simula Processamento Custoso

        ((IProgress<int>)report).Report(i * 10);
    }
}

A cada etapa concluída (iteração), reportamos o progresso através da instância da classe EventProgress<T>. O único detalhe interessante, é que a interface IProgress<T> foi implementada de forma explícita, o que nos obriga a fazermos o casting para conseguir acessar o método Report, informando o parâmetro que representa o status do progresso.

Só que quando trabalhamos com tecnologias como Windows Forms ou WPF, há uma certa dificuldade em fazer a "conversação" entre as threads envolvidas. Por padrão, essas tecnologias utilizam apenas uma única thread, que é criada durante o início do processo. Com isso, todos os controles são criados e gerenciados por essa thread específica. Quando delegamos o processamento assíncrono para uma segunda thread, ela não poderá acessar os controles, pois os mesmos tem afinidade com a thread de criação.

Sabendo desta limitação, a classe EventProgress<T> faz uso de um SynchronizationContext internamente. Essa classe tem como finalidade fornecer uma forma de enfileirar trabalhos a serem realizados em um contexto (thread) específico. Ao criar a instância da classe SynchronizationContext, ela estabelece um vínculo com a thread onde ela está sendo criada, e ao acessá-la dentro de uma segunda thread, ele encaminhará o trabalho para ser realizado na thread ao qual está vinculada.

Finalmente, ao instanciar a classe EventProgress<T>, ela captura o SynchronizationContext corrente. Com isso, quando invocamos o método Report, ele irá disparar o método que vinculamos ao evento ProgressChanged, através do método Post da classe SynchronizationContext, que despacha (também de forma assíncrona) a execução para a thread que criou os controles, que seguramente poderá acessá-los.

Tags: , , ,

.NET Framework | Async

O Framework possibilita, a Linguagem facilita

by Israel Aece 29. October 2010 00:25

Desde a primeira versão do .NET Framework, várias funcionalidades foram construídas e as linguagens (aqui C# e VB.nET) facilitam o acesso as mesmas, tornando a vida do desenvolvedor muito mais simples. Para mostrar um exemplo simples, podemos perceber que grande parte de nós não utiliza o tipo Int32 para declarar um inteiro. Optamos pela keyword int. Grande parte de nós não utiliza o tipo Boolean para declarar um valor boleano. Optamos pela keyword bool.

Já no .NET Framework 2.0, a Microsoft introduziu no C# os iteradores, que evita a escrita explícita de classes que implementam as interfaces IEnumerable<T> e IEnumerator<T>. Tudo o que ela fez foi criar uma keyword chamada yield, que elimina toda essa necessidade, abstraindo a "complexidade" que esta implementação possui.

Para ter um exemplo mais recente, podemos avaliar as expressões lambdas. Elas nada mais são do que uma forma muito mais simples de se escrever (não de entender) delegates. Salvo a sintaxe de cada linguagem, a Microsoft está tornando tarefas complexas de serem realizadas em um modo bem mais simples de se fazer, ou seja, com novas essas keywords e um jeito diferente de se escrever o mesmo código, grande parte do trabalho complexo agora fica sob responsabilidade do compilador, que avalia e gera a saída necessária para tudo isso funcionar.

Entre as tarefas mais complexas que existem dentro do .NET Framework hoje, é a programação assíncrona. Ela não é nada trivial, pois exige um conhecimento razoável de como as coisas funcionam nos bastidores, o uso do par de métodos BeginXXX/EndXXX, das formas de capturar o resultado (callback ou poll), exceções que são disparadas, etc. Devido a essa complexidade toda, a programação assíncrona foi o alvo da Microsoft para tornar essa tarefa bem mais amena.

Apesar de isso já correr há algum tempo nos bastidores, a Microsoft anunciou hoje no PDC, um novo modelo de programação assíncrona, que permite a escrita do código assíncrono de forma tão simples quanto ao código síncrono. A partir de agora, tudo o que precisamos fazer é declarar a função como sendo assíncrona, e isso é feito através de uma nova keyword chamada async. Além disso, a função também poderá retornar um tipo predefinido que é a instância da classe Task<T>, que como o nome já sugere, representa uma tarefa que está sendo executada.

public async Task<decimal> CalcularSalarioAsync(string empregado)
{
    var resultado = await new ServicoDeSalario().CalcularAsync(empregado);

    return resultado;
}

Analisando o código acima, podemos perceber que também surgiu uma nova keyword: await. Apesar de parecer que ela irá bloquear a thread até que seja finalizado, não é isso o que acontece. Na verdade, quando o compilador encontrar essa keyword, o que ele fará é determinar que o que virá dali para baixo, até o final do método, será o callback que deverá ser disparado quando o processo assíncrono for finalizado.

A finalidade de facilitar isso, é fazer com que a programação assíncrona dentro do .NET Framework seja tão simples quanto a programação síncrona e, consequentemente, tornar as aplicações mais interativas quando falamos daquelas que exigem muitas interações de UI, ou tornando mais simples quando precisamos escrever alguma funcionalidade de forma assíncrona do lado do servidor, como é o caso de páginas/controllers assíncronos do ASP.NET e serviços em geral.

Explorarei mais detalhadamente essa funcionalidade em futuros artigos. Por agora, foi apenas um overview do que está por vir, e também mostrar que algumas complexidades são, aos poucos, absorvidas pelas linguagens. Da mesma forma que as facilidades anteriores que foram criadas dentro das linguagens, esta também será incorporada, mas vale ressaltar que continua sendo importante o seu entendimento interno da mesma forma que é importante conhecer delegates, caso contrário, expressões lambdas serão completamente estranhas.

Tags: , ,

.NET Framework | Async | C#

Silverlight e a chamada assíncrona de serviços

by Israel Aece 22. June 2010 23:47

Em geral, podemos consumir os serviços WCF de duas formas: síncrona ou assíncrona. Na primeira opção, ao referenciar o serviço em uma aplicação cliente e invocar um das operações que ele disponibiliza, a comunicação será realizada e enquanto ela não retorna, a aplicação ficará bloqueada aguardando o resultado. Já no segundo modelo, ao realizar a chamada de forma assíncrona, a operação será realizada em um thread secundária, permitindo que a aplicação continue trabalhando enquanto o serviço é executado.

Como sabemos, o Silverlight tem cada vez mais espaço como sendo front-end de aplicações. Essa tecnologia recorre à serviços quando precisa buscar algum conteúdo remoto, como por exemplo, preencher os dados em um controle ListBox quando um botão for pressionado. Atualmente, a maioria desses serviços são construídos em WCF, e referenciados na aplicação Silverlight para permitir que a mesma interaja com o servidor para extrair os dados necessários para executar o seu trabalho.

Para aqueles que já utilizam essa técnica, já devem ter percebido que no Silverlight, só podemos consumir esses serviços de forma assíncrona, ou seja, recorrendo à uma segunda thread através do par de métodos BeginXXX/EndXXX ou através de eventos. Mas porque isso acontece ou porque é necessário?

O Silverlight possui apenas uma única thread, que é chamada de UI Thread, e como sabemos, operações de I/O bound, como é o caso da comunicação através da rede, são tarefas custosas e que podem levar um grande tempo para ser executada, e justamente por isso, se o Silverlight bloqueasse a thread de UI enquanto executa essa requisição, o host (que é o navegador), também seria bloqueado. Mesmo que você tente emular uma chamada síncrona, utilizando algum recurso primitivo de sincronização (como o ManualResetEvent), você teria problemas do mesmo jeito, já que quando invocar o método WaitOne desta classe, ele bloqueará a thread de UI para esperar o resultado, que nunca chegará, pois o resultado somente será entregue para o Silverlight quando a thread de UI não estiver executando nenhum código, que não é o caso aqui, e como já era de se esperar, teremos um deadlock.

Desde o proxy de serviços WCF até classes de baixo nível, como é o caso da WebClient, terão o mesmo comportamento, ou seja, deverão ser sempre acionadas através do modelo assíncrono. Tudo isso se deve ao fato de que os navegadores atuais implementam a NPAPI (Netscape Plugin Application Programming Interface). A NPAPI trata-se de uma API multi-plataforma desenvolvida pela Netscape que permite que plugins sejam utilizados dentro dos navegadores. Para que os plugins sejam considerados multi-plataforma, eles precisam seguir rigorosamente essa API, o que determina que métodos remotos sejam executados assincronamente, e como já percebemos, o Silverlight segue o que foi definido por ela.

Tags: ,

Async | WCF

Programação Assíncrona no ASP.NET MVC

by Israel Aece 25. January 2010 10:38

A Microsoft introduziu na versão 2.0 do ASP.NET WebForms uma funcionalidade chamada de páginas assíncronas, assunto qual já comentei bastante por aqui, e também fiz uma palestra no TechEd 2008 à respeito desse mesmo assunto. Para recapitular, e resumidamente falando, quando uma requisição chega para o IIS, o mesmo entrega para o ThreadPool da CLR, que utilizará uma thread para efetivamente executar aquela requisição.

Por padrão, as páginas são sempre síncronas, ou seja, enquanto aquela requisição não for finalizada, a thread não será liberada. O problema disso é que muitas vezes, as páginas executam processos pesados, como por exemplo, acesso à serviços, consultas em base de dados, etc. Essas tarefas são consideradas I/O-bound, ou seja, são tarefas que não dependem da máquina local, mas sim do processamento do computador remoto que hospeda o serviço/banco de dados, da latência da rede, etc. Neste tempo que a thread fica aguardando esse processo de I/O finalizar, ela poderia estar servindo outras requisições, também ASP.NET, mas que não fazem necessariamente acesso à recursos deste tipo, como por exemplo, páginas institucionais. Dependendo da demanda, é comum o usuário receber no navegador erros com as seguintes mensagens de erro: Server Unavaliable ou Server Too Busy (503).

As páginas assíncronas resolvem esse tipo de problema, ou seja, quando encontrar uma tarefa deste tipo, a executam em uma thread de I/O, devolvendo a thread para o ThreadPool, e dando a chance dela atender outras requisições. Quando o processo remoto finalizar, a thread de I/O é retornada com o resultado e, novamente, uma thread é apanhada do ThreadPool para finalizar a requisição, e que na maioria das vezes, irá renderizar o resultado.

Por ser algo que aumenta consideravelmente a performance, a Microsoft está introduzindo este mesmo recurso no ASP.NET MVC. A partir da versão 2.0 do mesmo, teremos a possibilidade de criar ações assíncronas. Aplicações MVC que estão sujeitas à uma grande quantidade de requisições, podem fazer as execuções das ações de forma assíncrona, trabalhando de forma bem semelhante ao WebForms, e dando também ao MVC o mesmo benefício.

O primeiro passo para a criação de ações assíncronas, é fazer com o controller herde da classe abstrata AsyncController e não apenas de Controller. Essa classe fornecerá toda a infraestrutura para que as ações sejam executadas assincronamente, mas é importante dizer que mesmo que o controller herde de AsyncController, ele ainda pode continuar executando ações síncronas. Ao contrário do modelo síncrono, ações assíncronas dependem de um par de métodos, sendo um que inicia a tarefa custosa, e o segundo que será disparado quando o processo for finalizado. Atualmente é necessário sufixar o nome da ação com os respectivos sufixos: "Async" e "Completed". Abaixo temos um exemplo de como fica a estrutura de um controller assíncrono:

public class UsuariosController : AsyncController
{
    public void ListagemAsync(int quantidade)
    {
        //...
    }

    public ActionResult ListagemCompleted(Usuario[] usuarios)
    {
        //...
    }
}

Repare que o primeiro método é definido como void, pois quem retorna o resultado para a View é o método que é disparado quando o processo for finalizado. Um outro detalhe importante é que o método sempre será referenciado ou acessado no navegador como "Listagem" e nunca como "ListagemAsync", pois os sufixos são somentes utilizados pelo runtime do ASP.NET.

Ao herdar da classe AsyncController, novas propriedades estão a nossa disposição, e uma delas é a AsyncManager, que retorna a instância de uma classe com o mesmo nome. Como o próprio nome diz, ela é a responsável por gerenciar as operações assíncronas. Essa classe fornece três propriedades: OutstandingOperations, Parameters e Timeout. A primeira delas, OutstandingOperations, retorna a instância de uma classe chamada OperationCounter, onde essa classe controla a quantidade de operações que foram inicializadas pela respectiva ação. Já a propriedade Parameters, serve como um dicionário de dados, que permite passar informações para o método de finalização (como o resultado, por exemplo) e, finalmente, a propriedade Timeout, onde podemos definir um número inteiro que representa a quantidade de milisegundos (padrão de 45000 (45 segundos)) que o ASP.NET irá aguardar até que a operação seja finalizado.

Como sabemos, o .NET Framework fornece duas formas para trabalho assíncrono: modelo APM (métodos Begin/End) ou o modelo de eventos. O MVC suporta as duas formas de trabalho, mas a implementação para cada uma delas é ligeiramente diferente. Independentemente de qual técnica você utilize para invocar, o método que é disparado quando o processo assíncrono é finalizado não mudará em nada. Abaixo temos a sua implementação, e podemos notar que ele nada sabe sobre questões assíncronas.

public ActionResult ListagemCompleted(Usuario[] usuarios)
{
    ViewData["Usuarios"] = usuarios;
    return this.View();
}

O próximo passo é codificar o método ListagemAsync, e vamos utilizar inicialmente o modelo de programação assíncrona do .NET (APM). Como exemplo, vamos consumir um serviço WCF que foi referenciado na aplicação. Lembre-se que ao referenciar um serviço WCF, por padrão, ele não traz as versões assíncronas das operações; para poder habilitá-las, consulte este artigo.

Vamos então instanciar o proxy para estabelecer o canal de comunicação entre a aplicação e o serviço WCF. Podemos notar que temos que obrigatoriamente invocar os métodos Increment e Decrement, expostos pela propriedade OutstandingOperations, para especificar a quantidade de operações assíncronas que estão em andamento. Depois disso, devemos inicializar a operação assíncrona, através do método BeginRecuperarUsuarios. De acordo com o modelo APM, além dos parâmetros exigidos pelo método em si, temos que informar um callback, que nada mais é do que o método que será executado quando o processo assíncrono for finalizado.

Note no código abaixo que dentro do método de callback, estamos recuperando o resultado (EndRecuperarUsuarios) e armazenando dentro da propriedade Parameters a coleção de usuários. O valor colocado dentro deste dicionário será passado para o método ListagemCompleted, através do parâmetro "usuarios". Em seguida estamos também decrementando o contador de operações assíncronas. Note que tudo o que foi descrito neste parágrafo, está sendo executado dentro do método chamado Sync, também fornecido pela propriedade AsyncManager. Isso é necessário para garantir que este código e, um pouco mais tarde, a execução do método ListagemCompleted, sejam disparados em uma thread que o ASP.NET terá o controle. Se você não se atentar à isso e tentar executar esse código, ainda estará em uma thread de I/O, fazendo com que o contexto do HTTP (HttpContext.Current) esteja nulo e, consequentemente, não conseguirá acessar grande parte dos recursos que precisa para exibir o resultado.

public void ListagemAsync(int quantidade)
{
    ServicoDeUsuarios proxy = new ServicoDeUsuarios();
    this.AsyncManager.OutstandingOperations.Increment();

    proxy.BeginRecuperarUsuarios(quantidade, ar =>
    {
        AsyncManager.Sync(() =>
        {
            this.AsyncManager.Parameters["usuarios"] = proxy.EndRecuperarUsuarios(ar);
            this.AsyncManager.OutstandingOperations.Decrement();
        });
    }, null);
}

Depois de visualizar a implementação baseada no modelo APM, temos agora o modelo de eventos. Neste caso não precisamos envolver o método Sync, pois o evento que determina que o processo foi finalizado já acontece dentro da thread do próprio ASP.NET. Tudo o que precisamos fazer é se vincular à este evento, e dentro dele armazenar o resultado na propriedade Parameters e decrementar o contador, tudo de forma bem parecida ao que vimos acima. Apenas para iniciar o processo assíncrono, você deverá invocar a versão assíncrona do método que é gerado durante a criação do proxy do WCF, que terá sempre o nome da operação sufixada com a palavra "Async".

public void ListagemAsync(int quantidade)
{
    ServicoDeUsuarios proxy = new ServicoDeUsuarios();
    this.AsyncManager.OutstandingOperations.Increment();

    proxy.RecuperarUsuariosCompleted += (sender, e) =>
    {
        this.AsyncManager.Parameters["usuarios"] = e.Result;
        this.AsyncManager.OutstandingOperations.Decrement();
    };

    proxy.RecuperarUsuariosAsync(quantidade);
}

Observação: Qual dos dois modelos utilizar? Isso vai depender da API que está sendo chamada dentro do controller/ação assíncrono. Se ela suportar os dois modelos, então você pode escolher um deles. Mas há situações que não temos esse luxo, como por exemplo, quando queremos invocar uma consulta no SQL Server usando o ADO.NET tradicional, ou até mesmo ler o conteúdo de um arquivo no disco. Essas classes apenas fornece o modelo APM, com um par de métodos Begin/End.

A Microsoft ainda disponibilizou dois atributos: AsyncTimeoutAttribute e NoAsyncTimeoutAttribute. O primeiro deles disponibiliza uma propriedade chamada Duration, que como falamos acima, recebe a quantidade de milisegundos que determina o timeout. Já o segundo atributo deve ser aplicado quando você quer deixar isso indefinido. Independentemente de qual irá utilizar, eles devem ser aplicados sempre ao método que está sufixado com a palavra "Async", assim como vemos abaixo:

[AsyncTimeout(Duration = 30000)]
public void ListagemAsync(int quantidade)
{
    //...
}

Conclusão: É importante dizer que esse modelo de programação, apesar de tornar o código um pouco mais ilegível e poluído, traz um grande benefício em termos de performance e escalabilidade. Não pense que a página aparecerá no navegador do usuário enquanto a ação é processada, e quando ela for finalizada, aparecerá os dados na tela. Em termos visuais, você ainda terá o mesmo resultado, ou seja, enquanto a ação não for finalizada o navegador ficará bloqueado até que a requisição como um todo seja concluída, mas esta funcionalidade irá "desafogar" o ASP.NET.

Tags: ,

ASP.NET | Async

Threading em WPF

by Israel Aece 24. November 2009 21:27

Quando desenvolvemos aplicações Windows, é muito comum em algum ponto da mesma, que algumas tarefas mais complicadas e custosas sejam realizadas, que podem levar um tempo maior até que seja concluída. Independentemente do que ela faça, seja um cálculo, uma consulta em uma base de dados ou uma chamada para um serviço, se você executar esse código de forma síncrona, o usuário deverá esperar até que essa tarefa seja finalizada, para a partir daí, conseguir acessar outras áreas do sistema.

Ao rodar uma aplicação Windows, um processo é criado dentro do sistema operacional. Processo não executa nenhum código; são as threads que fazem isso. Quando o processo é criado, uma thread é criada juntamente com ele, e esta é muitas vezes chamada de main-thread (thread principal). Essa thread nasce e morre com o término do processo, ou seja, enquanto ela estiver executando alguma tarefa, o processo continuará ativo.

As aplicações Windows que conhecemos, como Windows Forms, Console, Windows Services e WPF trabalham nesta mesma linha. Aplicações que possuem gráficos, como é o caso do Windows Forms e do WPF, tem um agravante: a afinidade que os controles tem com a thread principal. Quando a aplicação é iniciada, a thread principal é quem cria os controles (Form, TextBox, Label, TextBlock, etc.), e quando dizemos que há uma afinidade, isso quer dizer que podemos somente manipular esses controles, através da mesma thread que os criaram, e qualquer tentativa de fazer isso através de uma segunda thread, uma exceção do tipo InvalidOperationException será disparada.

Quando essas tarefas são finalizadas, é normal queremos exibir o resultado para o usuário, que na maioria das vezes, implica em alterar a propriedade Text de algum controle, exibir uma MessageBox, etc. Em Windows Forms, todos os controles herdam direta ou indiretamente da classe Control, que fornece um método chamado Invoke, e que dado um delegate, executa o método relacionado na mesma thread do criador. Mais tarde, com o .NET Framework 2.0, surgiu o SynchronizationContext, que facilitou bastante a atualização dos controles a partir de uma thread secundária.

Agora temos o WPF, que traz novas funcionalidades e uma forma um pouco diferente para lidar com esse tipo de problema. Vamos a partir deste artigo, explorar um pouco mais sobre o modelo de threading do WPF. A Microsoft introduziu uma série de novos tipos, espalhados por vários namespaces e que serão utilizados para conseguir atingir o nosso principal objetivo. Para iniciar, o primeiro tipo que temos que conhecer é a classe Dispatcher. Essa classe serve como um gerenciador de tarefas para serem executadas, e está sempre associada com uma determinada thread de UI. Ela mantém uma fila de tarefas que são executadas utilizando a thread qual está relacionada.

A fila que é mantida pela classe Dispatcher é priorizada, que permite especificar uma prioridade antes de enfileirar a tarefa (mais detalhes abaixo). Para alistar uma tarefa nesta fila, você poderá utilizar o método Invoke ou BeginInvoke. A diferença entre eles é clara: o primeiro executa a tarefa de forma síncrona, enquanto a segunda alista a tarefa para ser executada de forma assíncrona. E para sedimentar, ambas sempre executarão na thread ao qual o Dispatcher está vinculado.

Grande parte das classes que compõem o framework do WPF, incluindo os controles, herda direta ou indiretamente da classe abstrata DispatcherObject, que possui uma estrutura simples, ou seja, fornece uma propriedade chamada Dispatcher que retorna a instância de uma classe Dispatcher, e como já era de esperar, fornece a instância do Dispatcher que está vinculado com aquele classe/controle.

A classe DispatcherObject ainda fornece dois métodos importantes: CheckAccess e VerifyAccess. A diferença entre eles é que o primeiro retorna um valor boleano, indicando se a thread que está chamando tem direito de acesso ao Dispatcher correspondente. Já o segundo método, VerifyAccess, dispara uma exceção do tipo InvalidOperationException, caso a thread que está chamando não tiver direito de acesso ao Dispatcher. Como pudemos perceber, esses métodos vão nos auxiliar para determinar se há ou não a necessidade de atualizar o controle através da thread atual, sem que seja necessário utilizar o Dispatcher para chegar até o controle.

Para exemplificar, imagine que temos uma thread que executará algum cálculo complexo, e depois de calculado, deverá exibir o resultado em um TextBox. Como comentado acima, dentro desta thread não podemos alterar qualquer propriedade do TextBox, e para solucionar isso no WPF, vamos recorrer a propriedade Dispatcher do TextBox ("txt"), que foi herdada de DispatcherObject. Ao passar um delegate para o método Invoke, ele será executado (de forma síncrona) na mesma thread que criou o controle.

new Thread(() =>
{
    int result = 2 ^ 4 * 2 + 3 / 3;
    Thread.Sleep(3000); //Simula Processo Complexo

    txt.Dispatcher.Invoke(new Action<int>(r => txt.Text = r.ToString()), result);
}).Start();

Se desejar, podemos trocar o método Invoke por BeginInvoke, e a atualização do controle será feita em background. A vantagem desta técnica é que você pode executar a atualização do controle enquanto faz outras tarefas. É importante que você mantenha tarefas "leves" dentro do Dispatcher, pois tudo o que ele deveria fazer ali é atualizar a UI; colocar tarefas mais complexas, voltará a ter concorrência com os eventos dos controles e, consequentemente, o usuário voltará a ter os travamentos das telas do sistema, que acontecia quando trabalhávamos de forma síncrona.

Quando você opta por utilizar o método BeginInvoke, ele retorna uma instância da classe DispatcherOperation. Basicamente, este objeto representa uma espécie de "ponteiro" para a operação que está sendo executada. Essa classe fornece uma série de membros interessantes, e entre eles temos:

  • Dispatcher: O Dispatcher relacionado.
  • Priority: Uma das opções definidas no enumerador DispatcherPriority, que define a prioridade da operação.
  • Result: Retorna um System.Object com o resultado da tarefa (isso quando ela retornar algum resultado).
  • Status: Uma das opções definidas no enumerador DispatcherOperationStatus, que define o status atual da operação (Pending, Aborted, Completed ou Execution).
  • Abort: Método que aborta a operação que está sendo executada.
  • Wait: Quando invocado, fará um "join" na thread atual, aguardando até o término da operação. Opcionalmente você pode especificar um timeout.
  • Aborted: Evento que é disparado quando a operação é abortada.
  • Completed: Evento que é disparado quando a operação foi finalizada.

Com a instância do DispatcherOperation em mãos, podemos utilizar duas formas para chegar até o resultado, que é via eventos ou através de polling. Utilizando o modelo de eventos, podemos nos vincular ao evento Completed, e quando a operação for finalizada, esse evento será automaticamente disparado. Já o polling consiste em testar, de tempo em tempo, se a operação finalizou ou não. Abaixo temos os dois exemplos de utilização:

new Thread(() =>
{
    int result = 2 ^ 4 * 2 + 3 / 3;
    Thread.Sleep(3000); //Simula Processo Complexo

    DispatcherOperation op =
        txt.Dispatcher.BeginInvoke(new Action<int>(r => txt.Text = r.ToString()), result);

    op.Completed += (o, args) => MessageBox.Show("Finalizou Tudo");
}).Start();


new Thread(() =>
{
    int result = 2 ^ 4 * 2 + 3 / 3;
    Thread.Sleep(3000); //Simula Processo Complexo

    DispatcherOperation op =
        txt.Dispatcher.BeginInvoke(new Action<int>(r => txt.Text = r.ToString()), result);

    //Faz Algo...

    while (op.Status != DispatcherOperationStatus.Completed)
    {
        if (op.Wait(TimeSpan.FromSeconds(5)) == DispatcherOperationStatus.Completed)
        {
            //Finalizou a Atualização da UI
            break;
        }
    }
}).Start();

Tanto o método Invoke quanto o BeginInvoke possui versões (overloads) destes métodos que permitem especificar uma prioridade, e para isso, utilizamos uma das doze opções definidas pelo enumerador DispatcherPriority. Há algumas opções interessantes, como por exemplo Inactive, que permite você alistar a operação, mas que não será processada. De qualquer forma, na maioria das vezes a opção Normal (que também é o padrão), já será o suficiente. Abaixo um exemplo de como podemos proceder para especificar a prioridade de uma operação:

txt.Dispatcher.BeginInvoke(new Action<int>(r => txt.Text = r.ToString()), DispatcherPriority.Normal, result);

Uma vez que você tem operações alistadas no Dispatcher, o runtime irá determinar quando executá-las, dependendo da prioridade definida. A classe Dispatcher também fornece métodos para abortar todas as operações pendentes de processamento. Para isso, recorremos aos métodos InvokeShutdown ou BeginInvokeShutdown (a diferença entre eles já é sabida). A classe Dispatcher ainda fornece um evento chamado ShutdownFinished, que é disparado quando shutdown do Dispatcher for completamente finalizado, e com isso, você poderá tomar alguma decisão.

Conclusão: Como vimos, ambas tecnologias (Windows Forms e WPF) possuem os mesmos comportamentos. Como trabalhar com ambientes multi-threading não é uma tarefa fácil, a Microsoft introduziu no WPF uma forma diferente de se trabalhar para a atualizar de UI através de uma segunda thread. Além de um modelo suavemente diferente, há algumas melhorias internas que garantem que isso acabe sendo executado de uma forma melhor. E para finalizar, essa técnica visa sempre tornar a aplicação mais amigável para o usuário, sem que eles tenha experiências ruins.

Tags: ,

Async | WPF

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Indicações

Introdução ao ASP.NET Web API - e-Book