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

Mensagens Assíncronas com ChannelFactory

by Israel Aece 18. November 2009 10:41

Uma das formas que temos para consumir serviços WCF em um cliente qualquer, é utilizando a classe ChannelFactory<TChannel>, assim como eu já mostrei um exemplo neste post. Geralmente utilizamos esta técnica quando optamos por compartilhar os tipos (incluindo a interface que representa o contrato) entre o serviço e o cliente, evitando a publicação do documento WSDL, a reconstrução destes tipos do lado do cliente e, principalmente, a necessidade de efetuar a referência do serviço através da IDE do Visual Studio .NET.

Atenção: Antes de prosseguir a leitura deste post, aconselho que leia os seguintes artigos:

Quando fazemos a referência ao serviço através do Visual Studio, há uma opção chamada "Generate asynchronous operations", que para cada operação encontrada no serviço, um par de métodos BeginNomeDaOperacao e EndNomeDaOperacao serão criados, e que irão trabalhar em conjunto, para que assim, o cliente consiga invocar a respectiva operação de forma assíncrona. O problema disso é que muitas vezes o contrato não oferece suporte à chamadas assíncronas, o que nos obrigará a criar toda a infraestrutura do lado do cliente para suportar isso. Imagine que temos o seguinte contrato:

[ServiceContract]
public interface IContrato
{
    [OperationContract]
    string FazAlgo(string value);
}

Como não temos a versão assíncrona do método FazAlgo, temos que recorrer à delegates para conseguir efetuar a chamada. Sendo assim, o código do lado do cliente, poderia ficar da seguinte forma:

private ChannelFactory<IContrato> _factory;
private IContrato _proxy;

public Form1()
{
    this._factory =
        new ChannelFactory<IContrato>(
            new NetTcpBinding(),
            "net.tcp://localhost:8722/srv");

    this._proxy = this._factory.CreateChannel();
}

private void button1_Click(object sender, EventArgs e)
{
    string parametro = "algum valor";

    Func<string, string> executor = p => this._proxy.FazAlgo(p);

    executor.BeginInvoke(
        parametro,
        result =>
        {
            this.textBox1.Invoke(
                new Action<string>(valor => this.textBox1.Text = valor),
                ((Func<string, string>)result.AsyncState).EndInvoke(result));

        },
        executor);
}

A diferença que vemos no código acima, é que invocamos o método FazAlgo através de um delegate. Ao invés de criarmos delegates a todo momento para uma necessidade específica, podemos recorrer aos delegates expostos pelo .NET Framework Action<> e Func<>. No caso acima, estamos fazendo uso do Func<string, string>, que coincide com a assinatura do método FazAlgo, ou seja, recebe e devolve uma string. No exemplo acima, estamos fazendo o uso de lambda ao invés de explicitamente criar a instância do delegate.

Todos os delegates dão suporte à chamada assíncrono para método que ele mantém a referência, e sendo assim, via BeginInvoke disparamos a execução da operação do serviço, utilizando uma segunda thread, e mantendo a aplicação disponível para outros trabalhos. Neste caso, o método BeginInvoke recebe três parâmetros: o primeiro é o parâmetro de entrada que o serviço recebe; o segundo é um delegate de callback, que será invocado quando o resultado voltar; e finalmente, o terceiro parâmetro é um System.Object que será passado para o callback, e que estará acessível através da propriedade AsyncState, e que no caso, passamos a instância do delegate criado para invocar o método.

Para recuperar o resultado, dentro do callback invocamos o método EndInvoke, que retornará o resultado do serviço, e como na maioria dos casos, precisamos exibir isso na tela. Como o callback sempre é disparado na thread que está executando o método assíncrono, você não pode tocar nos controles, já que eles tem afinidade com a thread de criação deles. Aqui entra em cena o método Invoke, exposto pelo do controle que deseja atualizar, e através dele, determinamos um método para ser executado na mesma thread que o criou, e aqui também utilizando lambda.

Se você tiver acesso ao contrato e poder alterá-lo, então você pode dar suporte ao processamento assíncrono ao mesmo, e grande parte do trabalho que vimos acima, do lado do cliente, será descartado. Para suportar o processamento assíncrono no contrato, temos que criar as versões assíncronas do método, e com isso, o nosso contrato de exemplo ficará da seguinte forma:

[ServiceContract]
public interface IContrato
{
    [OperationContract]
    string FazAlgo(string value);

    [OperationContract(AsyncPattern = true)]
    IAsyncResult BeginFazAlgo(string value, AsyncCallback callback, object state);

    string EndFazAlgo(IAsyncResult result);
}

Ao contrário do que vimos anteriormente, ao utilizar essa técnica, você terá o processamento assíncrono tanto do lado do cliente, quanto do lado do serviço. Eu já discuti bastante sobre isso nestes outros artigos. Sendo assim, toda a complexidade da chamada assíncrona será movida para o serviço, que deveremos criar a versão assíncrona do método FazAlgo. O código do lado do cliente ficará relativamente mais simples:

private void button2_Click(object sender, EventArgs e)
{
    string parametro = "algum valor";

    this._proxy.BeginFazAlgo(
        parametro,
        result =>
        {
            this.textBox1.Invoke(
                new Action<string>(valor => this.textBox1.Text = valor),
                this._proxy.EndFazAlgo(result));
        }, null);
}

Para ajudar no entendimento, você pode baixar o código de exemplo clicando aqui.

Tags: , , ,

Async | WCF

Nova versão do método Monitor.Enter

by Israel Aece 26. October 2009 14:48

Desde a primeira versão do .NET Framework, temos a keyword lock. Com ela, podemos criar um bloco de código que será acessado por uma única thread de cada vez. Na verdade, depois de compilado, esse bloco é transformado em chamadas para os métodos Enter e Exit, respectivamente, expostos pela classe Monitor (System.Threading), e envolvido por um bloco try/finally. Imagine o seguinte código: 

lock (_meuLock)
{
    //código "protegido"
}

Ao ser compilado, esse código será transformado em:

Monitor.Enter(_meuLock);
try
{
    //código "protegido"
}
finally
{
    Monitor.Exit(_meuLock);
}

O problema disso é que, segundo o Joe Duffy, exceções podem acontecer entre o método Enter (que é responsável por adquirir o lock) e o bloco try; isso dará origem ao que ele chama de "bloqueios orfãos", já que nunca serão liberados, pois o bloco finally não será disparado.

Para resolver isso, a Microsoft criou uma nova versão (overload) do método Enter, que além do objeto que representa o lock, recebe um parâmetro boleano indicando se o lock foi ou não adquirido com sucesso. O valor retornado por esse parâmetro boleano, será avaliado mais tarde, que determinará se o método Exit correspondente deverá ou não ser invocado. Além dessa mudança, o método Enter passa a ser chamado dentro do bloco try, garantindo assim que o bloco finally seja disparado e, consequentemente, liberando o lock. O mesmo código acima, a partir da versão 4.0 do .NET Framework, passa a ser compilado da seguinte forma:

bool taken = false;
try
{
    Monitor.Enter(_meuLock, ref taken);
    //código protegido
}
finally
{
    if (taken)
        Monitor.Exit(_meuLock);
}

Tags:

.NET Framework

Novas classes para inicialização de objetos

by Israel Aece 21. October 2009 06:57

Quando desenvolvemos algum tipo de aplicação ou componente, é muito comum encontrarmos dentro do nosso código, classes que são extremamente custosas, ou melhor, que possuem um grande overhead na inicialização, como por exemplo, efetuam acesso à IO, cálculos complexos, etc. Dependendo da situação, instanciamos essas classes (pagando o alto preço da inicialização) e não utilizamos, já que, eventualmente, o teu sistema não precisará dela naquele momento.

Para melhorar isso, a Microsoft está disponibilizando no .NET Framework 4.0, uma classe genérica chamada Lazy<T> (namespace System). Basicamente, a finalidade desta classe é postergar, ao máximo, a criação do teu objeto, ou seja, isso somente acontecerá quando você realmente precisar dele. Por ser uma classe genérica e o parâmetro T não ter qualquer restrição, você pode definir T como qualquer tipo. Essa classe será um wrapper para o teu objeto custoso, efetuando a criação do mesmo somente quando for requisitado.

Ao instanciar a classe Lazy<T>, você tem algumas opções que variam de acordo com o overload do construtor que utiliza. Em um dos construtores, há um parâmetro boleano, que determina se a inicialização será ou não thread-safe. Se a instância da classe Lazy<T> pode ser acessada por um ambiente multi-threading, então definir este valor como True (que é o padrão), evitará problemas conhecidos, tal como as races conditions. Com isso, a primeira thread que entrar, incializará o objeto, e as threads subsequentes compartilharão o mesmo objeto, que já está criado. Mas se ambiente multi-threading não é o cenário, então definir esse parâmetro como False evitará processamentos extras, que são desnecessários neste caso.

Há também um overload do construtor, que recebe como parâmetro a instância de um delegate do tipo Func<T>. Esse delegate é referido como uma "factory", ou seja, apontará para um método responsável por criar o objeto quando for solicitado, nos permitindo inicializá-lo de acordo com uma regra específica. Quando esse parâmetro não é informado, a classe Lazy<T> irá instanciar o tipo através do método CreateInstance da classe Activator, obrigando o tipo definido em T, a ter um construtor público sem parâmetros, caso contrário, uma exceção será disparada.

Além dos construtores, essa classe ainda expõe, publicamente, duas propriedades de somente leitura: IsValueCreated e Value. A primeira delas, retorna um valor boleano indicando se o objeto já foi ou não criado. Já a segunda, é a propriedade que utilizamos para extrair o objeto que foir criado e está sendo gerenciado pelo wrapper. É dentro desta propriedade que há toda a regra utilizada para determinar se o objeto já foi criado. E como vimos acima, caso ele ainda não tenha sido, invocará o método privado LazyInitValue, e me retornará a instância. Chamadas subsequentes, da mesma thread ou não, não entrarão mais neste método, reutilizando a instância criada. O código abaixo exibe um exemplo da utilização desta classe:

public class NotaFiscal
{
    public int Codigo { get; set; }
    public DateTime Data { get; set; }

    private Lazy<List<Item>> _itens;

    public NotaFiscal(int codigo)
    {
        this.Codigo = codigo;
        this._itens =
            new Lazy<List<Item>>(() => DataHelper.RecuperarItensDaNotaFiscal(this.Codigo));
    }

    public IEnumerable<Item> Itens
    {
        get
        {
            return this._itens.Value;
        }
    }
}

Como podemos perceber no código acima, a instância da classe representa uma Nota Fiscal. Muitas vezes carregamos a Nota Fiscal completa, incluindo seus respectivos itens, mas nem sempre eles são utilizados. Ao invés de carregar esses itens na criação da Nota Fiscal, iremos postergar essa tarefa, recorrendo a classe Lazy<T>. Como podemos ter vários itens, então o argumento T será definido como List<Item>. No construtor da classe Nota Fiscal, instanciamos a classe Lazy<List<Item>>, definindo em seu construtor, o método responsável por carregar os itens da Nota Fiscal. A propriedade que expõe os itens da Nota Fiscal, quando solicitada, recorre a propriedade Value do objeto _itens, que como vimos acima, é neste momento que o método (factoryRecuperarItensDaNotaFiscal será disparado.

Além da classe Lazy<T>, ainda temos a classe ThreadLocal<T> (namespace System.Threading). Assim como a anterior, não há nenhuma restrição quanto ao argumento T, ou seja, podemos definir qualquer tipo. A finalidade desta classe, é sanar alguns comportamentos de campos estáticos quando utilizados em conjuto com o atributo ThreadStaticAttribute. Ao aplicar esse atributo, cada thread terá a sua própria cópia do valor, mesmo que ele seja declarado como estático (static). O problema que ocorre ao utilizar esse atributo, é quando temos um campo que já é automaticamente inicializado, como por exemplo:

public class Teste
{
    [ThreadStatic]
    public static int Numero = 4;
}

A inicialização de todo membro estático ocorre apenas uma única vez, mesmo quando temos este atributo aplicado. Para ilustrar isso, vamos criar três threads diferentes, onde cada uma delas escreverá o valor do membro Numero:

new Thread(() => Console.WriteLine(Teste.Numero)).Start();
new Thread(() => Console.WriteLine(Teste.Numero)).Start();
new Thread(() => Console.WriteLine(Teste.Numero)).Start();

E o resultado é: 4, 0 e 0. A classe ThreadLocal<T> vai conseguir lidar com isso, ou seja, permitirá especificar um delegate de inicialização, que será invocado sempre que o valor for requisitado por uma nova thread. Ao invés da inicialização acontecer uma única vez, ele sempre rodará quando solicitado, e sempre trazendo a cópia da informação para dentro da thread corrente, não compartilhando o mesmo, assim como já acontecia anteriormente. Com essa nova classe, podemos reescrever o exemplo da seguinte forma:

public class Teste
{
    public static ThreadLocal<int> Numero = new ThreadLocal<int>(() => 4);
}

new Thread(() => Console.WriteLine(Teste.Numero.Value)).Start();
new Thread(() => Console.WriteLine(Teste.Numero.Value)).Start();
new Thread(() => Console.WriteLine(Teste.Numero.Value)).Start();

E agora, como já era de se esperar, temos como resultado: 4, 4 e 4. É importante dizer que se nada for informado no construtor desta classe, ela sempre construirá o tipo com o seu valor padrão.

Para finalizar, temos a classe estática LazyInitializer, que possui apenas um único método público: EnsureInitialized. Neste caso, ao invés de definir todos os membros como Lazy<T>, podemos recorrer a este método para inicializá-los individualmente, quando você achar necessário, sem a necessidade do wrapper. O código abaixo ilustra a utilização desta técnica, mas repare que informamos o objeto que será abastecido e o método (factory) que o construirá.

public IEnumerable<Item> Itens
{
    get
    {
        if (this._itens == null)
            LazyInitializer.EnsureInitialized(ref _itens, () => DataHelper.RecuperarItensDaNotaFiscal(this.Codigo));

        return this._itens;
    }
}

Conclusão: Criar aplicações multi-threading é bem simples, mas o grande problema sempre é a sincronização delas. Classes como essas que vimos aqui, auxilia bastante neste caso, tirando em algum pontos, a responsabilidade do usuário em gerenciar isso, podendo ele se preocupar cada vez mais com as regras de negócio. Essas classes que vimos aqui, estarão disponíveis a partir da versão 4.0 do .NET Framework, que já trará também uma grande API para suportar o desenvolvimento de código paralelo.

Tags:

.NET Framework

Métodos Sincronizados

by Israel Aece 25. September 2009 12:00

Quando criamos uma classe e dentro dela colocamos métodos, estes podem ser acessados por múltiplas threads ao mesmo tempo. Caso você queira prevenir isso, ou seja, permitir que somente uma única thread por vez o acesse, você pode fazer uso de métodos sincronizados. Tudo o que você precisa fazer para isso funcionar, é decorar o método com o atributo MethodImplAttribute (namespace System.Runtime.CompilerServices), definindo em seu construtor a opção Synchronized, fornecida pelo enumerador MethodImplOptions, assim como é mostrado abaixo:

public class Processo
{
    [MethodImpl(MethodImplOptions.Synchronized)]
    public void Metodo()
    {
        //...
    }
}

Com isso, você terá a garantia de que somente uma thread por vez o executará, enquanto as outras que, eventualmente, cheguem a este mesmo método, ficarão em uma fila, esperando com que a thread atual finalize o seu trabalho.

Apesar de “proteger” as informações, você limitará o acesso à sua classe/tipo. Quando este atributo é aplicado à um método de instância, ele efetuará o bloqueio da instância como um todo (lock(this) { }); já se aplicá-lo em um método estático, ele bloqueará o tipo (lock(typeof(Processo)) { }). A implementação é fácil, mas você será prejudicado com os efeitos colaterais, pois terá maior contenção. Para melhorar o throughput, o ideal é você utilizar alguma técnica de locking que o próprio .NET Framework fornece, para assim refinar o bloqueio da sua classe/tipo.

Tags: ,

.NET Framework

Host de Serviços em Aplicações Windows

by Israel Aece 24. September 2009 21:42

Uma das grandes vantagens do WCF é que qualquer aplicação pode servir como hosting para ele. Isso quer dizer que além do tradicional IIS, podemos recorrer à outros tipos, como Windows Services, Console, Windows Forms ou WPF. Cada uma tem as suas vantagens e desvantagens, mas isso já foi abordado neste artigo.

Cada uma dessas opções de hosting, possuem implementações diferentes, e aplicações Windows Forms e WPF tem um sofrimento maior em relação às outras. Isso se deve ao fato de que aplicações Windows não gerenciam apenas o serviço, mas também há uma interface (formulários) para controlar. Ao utilizar um host próprio, temos que recorrer à classe ServiceHost que gerenciará a vida e a execução do serviço. O problema já começa aqui, ou seja, onde criar e manter a instância desta classe?

Antes de prosseguirmos, precisamos analisar o que são os Synchronization Contexts. Synchronization Contexts permite executarmos uma determinada tarefa em uma outra thread, diferente da qual estamos atualmente, representando uma espécie de “canal” entre as duas threads envolvidas, fazendo tudo o que for necessário para isso. Dentro do namespace System.Threading existe uma classe chamada SynchronizationContext, que representa esse “canal”, e o WCF interage com ele através da propriedade boleana UseSynchronizationContext do atributo ServiceBehaviorAttribute, que por padrão é True.

Na configuração padrão, quando criamos o ServiceHost e invocamos o método Open, o WCF irá verificar se há um synchronization context definido e o utilizará. Quando não existir ou a propriedade UseSynchronizationContext é definida explicitamente como False, as operações serão executadas em uma outra thread, que são extraídas do ThreadPool.

Como o Windows Forms cria automaticamente o SynchronizationContext no construtor de um formulário, quando criamos a instância da classe ServiceHost dentro dele, o WCF irá executar todas as operações nesta mesma thread (que também atende aos comandos do usuário (message loop)), pois o contexto já está criado. O exemplo abaixo ilustra isso:

public partial class CadastroDeClientes : Form
{
    private ServiceHost _host;

    public CadastroDeClientes()
    {
        //Neste momento, o context já está criado
        this._host = new ServiceHost(typeof(ServicoDeClientes), new Uri[] { });
    }
}

O problema desta técnica é que você sobrecarregará a thread de UI, já que ela terá que se preocupar com as operações do serviço e com os eventos tradicionais do formulário. Se você optar por abrir o host antes da chamada de um formulário, como por exemplo, dentro do método Main da aplicação, você ainda não terá o contexto estabelecido. Neste caso, qualquer manipulação que você, eventualmente, faça nos controles do formulário dentro das operações do serviço, precisarão de um tratamento especial, pois você não poderá manipular os controles em uma thread diferente da qual eles foram criados.

Finalmente, se você estiver com a propriedade UseSynchronizationContext definida como True (que é o padrão), e estiver consumindo o serviço no mesmo formulário que possui o ServiceHost criado, você terá problemas. Isso se deve ao fato de que a chamada para o serviço bloqueia a thread de UI, enquanto o WCF posta a mensagem para essa mesma thread para invocar o serviço (que está ocupada). Sendo assim, o deadlock será garantido.

Tags: , ,

WCF

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

Host