Vou limitar o escopo desse post a um método de uma classe que possui somente um parâmetro e que retorna um valor.
Na maioria das linguagens de programação por padrão um método é executado de forma síncrona, ou seja, quando chamamos o método este é executado e somente quando termina é que a linha de execução do programa continua.
Por exemplo, no código abaixo a mensagem "Terminei !" só é mostrada quando o método FacaAlgumaCoisa é finalizado.
Classe objeto = new Classe(); objeto.FacaAlgumaCoisa(); Console.WriteLine("Terminei !")
Digamos que NÃO queremos que o usuário aguarde que o método FacaAlgumaCoisa termine para continuar a execução do programa. Queremos então estar aptos a utilizar o programa e fazer outras operações enquanto o método é executado. Temos então que desenvolver uma aplicação que seja multi-tarefa ou multi-threading em inglês.
Não quero entrar muito a fundo no conceito de multi-threading e deixo esse estudo como "dever de casa".
O código abaixo será uma aplicação console C# em Visual Studio 2008 Professional SP1 usando o .NET 3.5. É possível também utilizarmos a versão Express sem problemas.
Vamos começar criando uma classe qualquer que iremos utilizar em nosso exemplo. Para simplificar, vamos criar a classe no arquivo Program.cs mesmo.
public class MinhaClasse { // exemplo de método que demora em torno de 10 segundos // para ser executado e retorna um valor public int MetodoDemorado(int a) { System.Threading.Thread.Sleep(10000); return a + 10; } }
A classe é bem simples e contém um método que possui um parâmetro do tipo int e retorna o resultado da soma deste com o valor 10. O método estático Sleep da classe Thread foi introduzido no exemplo somente para simular um processamento que leva em torno de 10 segundos para executar.
Vamos declarar então no método Main um objeto da classe MinhaClasse.
// cria instância da classe MinhaClasse MinhaClasse objeto = new MinhaClasse();
A idéia deste post é executar o método MetodoDemorado de forma assíncrona. Para executar um método de forma assíncrona precisamos de um delegate. Delegate é um tipo que referencia um método, similar aos ponteiros para funções do C/C++ (você já deveria saber disso !).
Felizmente no .NET 3.5 temos, com ajuda de Generics, alguns delegates que encapsulam alguns métodos. Neste exemplo vou utilizar o delegate System.Func
Criamos então a instância do delegate passando o método MetodoDemorado como parâmetro, conforme o código abaixo.
// encapsula o método em um delegate Func<int, int> async = new Func<int, int>(objeto.MetodoDemorado);
Obs: Se o método não retornasse valor poderíamos utilizar o delegate System.Action.
Para executar o método de forma assíncrona vamos utilizar o método do delegate BeginInvoke gerado pelo compilador. Teremos que passar então como argumentos um inteiro, que é o parâmetro do método MetodoDemorado, um método callBack chamado quando o método finalizar a execução e um objeto que representa um estado qualquer.
async.BeginInvoke(...);
O argumento para o método callBack é uma expressão lambda que representa um delegate anônimo, ou seja, um delegate que recebe um parâmetro IAsyncResult e não retorna valor. No código acima, r é o parâmetro IAsyncResult. Além disso, estamos passando o valor 10 como argumento do método MetodoDemorado e null para o estado.
async.BeginInvoke(10, (r) => { ... // aqui é o corpo do callBack }, null);
Utilizamos o método EndInvoke do delegate async para pegar o valor retornado pelo método MetodoDemorado. Após a execução do método, mostramos uma mensagem com o resultado.
// executa o método de forma assíncrona async.BeginInvoke(10, (r) => { // resultado do método int resultado = async.EndInvoke(r); // mostra resultado Console.WriteLine("Resultado: {0}", resultado); }, null);
Vamos incluir após o método BeginInvoke uma mensagem para que a aplicação console não seja finalizada e também vamos utilizar o método ReadKey que irá aguardar com que o usuário clique em uma tecla para finalizar a aplicação.
Segue abaixo portanto o código completo.
using System; namespace MetodoAssincrono { public class MinhaClasse { // exemplo de método que demora em torno de 10 segundos // para ser executado e retorna um valor public int MetodoDemorado(int a) { System.Threading.Thread.Sleep(10000); return a + 10; } } class Program { static void Main(string[] args) { // cria instância da classe MinhaClasse MinhaClasse objeto = new MinhaClasse(); // encapsula o método em um delegate Func<int, int> async = new Func<int, int>(objeto.MetodoDemorado); // executa o método de forma assíncrona async.BeginInvoke(10, (r) => { // resultado do método int resultado = async.EndInvoke(r); // mostra resultado Console.WriteLine("Resultado: {0}", resultado); }, null); Console.WriteLine("Aguardando execução do método..."); Console.ReadKey(true); } } }
Se executarmos essa aplicação, na tela de prompt será informado...
Só aqui já podemos perceber que a execução do método foi assíncrona, visto que a mensagem encontra-se após o BeginInvoke. Se aguardarmos um pouco (+- 10 segundos) poderemos ver a mensagem de retorno do método MetodoDemorado.
Espero ter sido claro e conseguido compartilhar com vocês esse tipo de técnica. Vou criar futuramente um post semelhante em uma aplicação Windows Forms tratando operações cross-thread.
Segue abaixo alguns links interessantes:
Delegate (C# Programming Guide)
http://bit.ly/54QbSD
Lambda Expressions (C# Programming Guide)
http://bit.ly/87xAZJ
Anonymous Delegates (C# Programming Guide)
http://bit.ly/6jZdYS
Func
http://bit.ly/5nAva4
Asynchronous Programming Using Delegates
http://bit.ly/6tcwzD
Boa iniciativa Ari!
ResponderExcluirPoderia ser Java, mas como orientação a objetos é td igual, também dá pra aplicar.. rs
Oi Guilherme,
ResponderExcluirPois é, na verdade não trabalho com Java há um bom tempo (trabalhei com a versão 1.0) e não sei o que evoluiu na linguagem. Pelo que pesquisei, aparentemente Java não possui delegates, acredito que AMI só com threads mesmo.
Um abraço !
Olá Ari, muito claro seu artigo.
ResponderExcluirParabéns pela iniciativa !
Abraço !