quarta-feira, 28 de março de 2012

Concorrência parte II

Continuando o assunto das THREADS ...

Durante a execução de threads pode ser necessário garantir que uma thread conclua a sua tarefa para que outra possa continuar.

Isso é possível com recurso ao método Join da classe Thread, chamando este método pára a execução da thread corrente, ou seja, a thread que chamou o método, até que termine a thread sobre a qual se chamou o método.

Por vezes este método pode provocar resultados inesperados especialmente se chamado a partir da thread principal do programa uma vez que a interface deixa de responder, incluindo os próprios timers.




Neste programa o botão Iniciar demo corre o seguinte código:


            obj_teste = new teste(); //objecto com código da thread
           
             thr_teste = new Thread(new ThreadStart(obj_teste.executa));  //nova thread criada
           
            this.timer1.Enabled = true;  //activar o timer para ver o estado da thread
            //vamos iniciar a thread
            thr_teste.Start();
           
            this.label3.Text="Em execução!";
            Application.DoEvents();  //permite que a aplicação actualize a interface
            if (this.checkBox1.Checked)   //activar o join sim ou não?
            {
                //vamos esperar que termine
                //nem o timer é chamado
                thr_teste.Join();
            }
            ////quando termina continua aqui
            this.label3.Text="Terminado!";
            Application.DoEvents();

O código da classe Teste é muito simples


    public class teste
    {
        public int valor;
        public teste()
        {
            valor = 0;
        }
        public void executa()
        {
            for (int i = 0; i <= 100; i++)
            {
                valor = i;
                Application.DoEvents();
                Thread.Sleep(100);
            }
        }
    }

Este código é só para alterar o valor da progress bar.

Depois de testar o programa é possível verificar que com o join activado a interface só responde quando a thread termina, não permitindo a utilização de nenhum objecto mesmo as simples textbox's.


segunda-feira, 26 de março de 2012

Concorrência

Na era dos processadores multicore a execução concorrente faz cada fez mais sentido, de que serve ter um processador com 8 núcleos se os nossos programas só correm uma tarefa de cada vez, ocupando assim um só núcleo.

Cada vez mais é necessário pensar os programas como pequenas tarefas e quando possível executar essas tarefas concorrentemente, ou seja, em simultâneo, porquê esperar que o disco guarde os dados quando podemos criar um thread que executa a tarefa sem interromper a interface.

A classe THREAD é, na plataforma .NET, o ponto de partida para a execução concorrente.
Com esta classe é possível definir prioridades de execução, pausar e retomar as threads bem como sincronizar diferentes threads entre si, criando relações de execução entre diferentes processos.



Para criar uma nova thread na plataforma .NET o programador precisa de ter uma classe com um método que não possua parâmetros de entrada e que devolva void, ou seja não devolve nada.
É este método que será executado quando a nova thread for iniciada.

Para este post vamos criar uma interface que deverá demonstrar a execução concorrente de três threads, para isso construimos o único form do nosso programa assim:

Os numericupdown vão servir para indicar o tempo de espera de cada thread, assim o valor máximo é 1000, que corresponde a um segundo. Quanto maior este valor mais lento será o enchimento da barra.

As progressbar devem ter um valor máximo de 100, que é o valor por defeito.
A ideia é que cada progressbar vá enchendo de cada vez que o código da thread completa um ciclo de execução.

Agora a classe das threads:

    class tarefa
    {
        private int tempo;  //tempo de espera por cada ciclo de execução
        public int valor;    //valor actual da thread
        private int valormax;   //valor máximo que interrompe a execução da thread
       
        //construtor
        public tarefa(int tempo, int valormax)
        {
            valor = 1;
            this.tempo = tempo;
            this.valormax = valormax;
        }
        //método onde a execução da thread ocorre
        public void executa()
        {
            while (true)
            {
                Thread.Sleep(tempo);
                valor++;
                if (valor >= valormax) break;  //aqui para o ciclo e a thread "morre"
            }
        }
    }

Um clique no botão Iniciar demo vai executar o seguinte código:

//Esta linha cria um objecto da classe tarefa

            t1 = new tarefa((int)this.numericUpDown1.Value,(int)this.progressBar1.Maximum);
//Esta linha cria uma thread associada ao método executa do objecto
            thr1 = new Thread(new ThreadStart(t1.executa));
//As restantes linhas criam mais dois objectos
            t2 = new tarefa((int)this.numericUpDown2.Value,  (int)this.progressBar2.Maximum);
            thr2 = new Thread(new ThreadStart(t2.executa));
            t3 = new tarefa((int)this.numericUpDown3.Value,  (int)this.progressBar3.Maximum);
            thr3 = new Thread(new ThreadStart(t3.executa));
//Estas linhas iniciam a execução das três threads
            thr1.Start();
            thr2.Start();
            thr3.Start();
            timer1.Enabled = true;

O botão Parar para as três threads:

            thr1.Abort();
            thr2.Abort();
            thr3.Abort();

O botão Pausar/Continuar pausa as threas e remota a sua execução:

            if (this.button3.Text == "Pausar")
            {
                thr1.Suspend();
                thr2.Suspend();
                thr3.Suspend();
                this.button3.Text = "Continuar";
                this.button2.Enabled = false;
            }
            else
            {
                thr1.Resume();
                thr2.Resume();
                thr3.Resume();
                this.button3.Text = "Pausar";
                this.button2.Enabled = true;
            }

O método Suspend coloca a execução em suspenso e o método Resume retoma a execução.

Por fim adicionei um timer para actualizar as progressbar refletindo os valores das threads.

O projecto está aqui.




domingo, 18 de março de 2012

Encriptar e Desencriptar

Com o .Net podemos facilmente codificar e descodificar mensagens através do namespace Cryptography.
Neste post vamos estudar um programa que encripta um texto com base numa palavra passe e posteriormente descodifica a mensagem.

Começamos por criar uma função para encriptar:
string codifica(string mensagem, string ppasse)
{
  //este vector vai ficar com a mensagem encriptada
  byte [] mensagem_codificada;
 //vamos utilizar codificação UTF8
  System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
  // Vamos utilizar o algoritmo MD5 para fazer o Hash da palavra passe
  MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
  //Agora podemos então fazer o hash da palavra passe
  byte[] TDESChave = HashProvider.ComputeHash(UTF8.GetBytes(ppasse));
  //De seguida vamos escolher o algoritmo de encriptação
  TripleDESCryptoServiceProvider TDESAlgoritmo = new TripleDESCryptoServiceProvider();

  //Configuração do algoritmo
  TDESAlgoritmo.Key = TDESChave;
  TDESAlgoritmo.Mode = CipherMode.ECB;
  TDESAlgoritmo.Padding = PaddingMode.PKCS7;


  //Para podermos converter a string primeiro temos de a converter para um vector
  byte[] dados = UTF8.GetBytes(mensagem);
  //Finalmente podemos codificar a mensagem
  try{
    ICryptoTransform codificador = TDESAlgoritmo.CreateEncryptor();
    mensagem_codificada = codificador.TransformFinalBlock(dados,0,dados.Length);
  }finally{
    //Agora limpamos a casa
    TDESAlgoritmo.Clear();
    HashProvider.Clear();
  }
  //Só falta devolver a mensagem codificada
  return Convert.ToBase64String(mensagem_codificada);
}

Para descodificar o processo é muito parecido e aqui fica o código:

string descodifica(string mensagem, string ppasse)
{
   //este vector vai ficar com a mensagem desencriptada
   byte[] mensagem_descodificada;
   //Codificaçao utilizada
   System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
   // Vamos utilizar o algoritmo MD5 para fazer o Hash da palavra passe
   MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
   //Agora podemos então fazer o hash da palavra passe
  byte[] TDESChave = HashProvider.ComputeHash(UTF8.GetBytes(ppasse));

  //o algoritmo de encriptação
  TripleDESCryptoServiceProvider TDESAlgoritmo= new TripleDESCryptoServiceProvider();

   //Configuração do algoritmo
    TDESAlgoritmo.Key = TDESChave;
    TDESAlgoritmo.Mode = CipherMode.ECB;
    TDESAlgoritmo.Padding = PaddingMode.PKCS7;
    //Para podermos converter a string primeiro temos de a converter para um vector
    byte[] dados = Convert.FromBase64String(mensagem);
    //Vamos descodificar a amensagem
    try
    {
          ICryptoTransform descodificador = TDESAlgoritmo.CreateDecryptor();
          mensagem_descodificada = descodificador.TransformFinalBlock(dados, 0, dados.Length);     
    }
    finally
    {
                // Por fim limpeza geral
                TDESAlgoritmo.Clear();
                HashProvider.Clear();
     }
     // Devolver a mensagem
     return UTF8.GetString(mensagem_descodificada );
}

E a interface é esta beleza

domingo, 11 de março de 2012

XML - RSS Reader em VB.Net

No seguimento dos dois post anteriores vamos agora criar um RSS Reader com o Visual Basic 2010.
A interface será assim


Componentes: um TableLayoutPanel (para organizar a interface), uma combobox (permite escolher a origem da feed RSS), uma listbox (onde apresentamos os títulos dos artigos), um label (sempre útil para mostrar estado e outras informações), um botão (permite iniciar o processo de download do ficheiro XML com a lista de artigos) e por fim um WebBrowser que permitirá visualizar as páginas dentro da nossa aplicação.

O código:
A parte principal do código está localizado no botão e tem o seguinte aspeto:
        Dim ligacao As String
        Dim doc As New XmlDocument
        Dim items As XmlNodeList

        If Me.ComboBox1.SelectedIndex = -1 Then
            Me.Label1.Text = "Tem de selecionar a fonte de RSS!"
            Exit Sub
        End If

        If Me.ComboBox1.Text = "PCMagazine" Then
            ligacao = "http://rssnewsapps.ziffdavis.com/pcmag.xml"
        Else
            ligacao = "http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml"
        End If

        Me.Label1.Text = "A carregar RSS! Aguarde..."
        Application.DoEvents()

        Me.ListBox1.Items.Clear()
        doc.Load(ligacao)
        items = doc.SelectNodes("//item")
        Try
            For Each item As XmlNode In items
                'titulo
                Me.ListBox1.Items.Add(item.SelectSingleNode("title").InnerText)
                'link
                lista.Add(item.SelectSingleNode("link").InnerText)
            Next
            Me.Label1.Text = "Concluído!"
        Catch ex As Exception
            Me.Label1.Text = "Erro de ligação"
        End Try

Comparando com o código C# do post anterior é possível ver que é muito semelhante, à exceção da parte que lida com a listbox.

O clique num artigo da lista vai, então, carregar a página web correspondente no webcontrol, e o código que permite essa tarefa é:


        If ListBox1.SelectedIndex = 0.1 Then Exit Sub

        Dim ligacao As New Uri(lista(ListBox1.SelectedIndex))
        Me.Label1.Text = ListBox1.Text
        Me.WebBrowser1.Url = ligacao

E pronto assim temos um leitor de feeds RSS muito simples mas sempre útil.

O projeto.

quarta-feira, 7 de março de 2012

XML - Leitor RSS Feeds

Neste poste vou publicar uma aplicação desenvolvida com o Monodevelop em C# que permitirá fazer o download das feeds de dois sites (BBC e PCMagazine) cujos artigos podem ser lidos no browser.
A janela principal é constituída por duas combobox's, uma que permite escolher qual o site de onde descarregar o XML com a lista de artigos (feed RSS) e a outra que é preenchida com a lista dos artigos depois de clicado o botão Carrega RSS.




Para carregar o ficheiro XML vamos utilizar o código


XmlDocument doc = new XmlDocument();

para criar um documento XML. Para depois ler o ficheiro do site selecionado na combobox1

if (this.combobox1.ActiveText=="BBC")
doc.Load("http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml");
else
if (this.combobox1.ActiveText=="PCMagazine")
doc.Load("http://rssnewsapps.ziffdavis.com/pcmag.xml");

Se não tiver nada selecionado na combobox1 apresentamos uma mensagem ao utilizador, para isto basta o seguinte código


MessageDialog md = new MessageDialog(this,
        DialogFlags.DestroyWithParent, MessageType.Info,
        ButtonsType.Close, "Tem de selecionar uma feed!");
md.Run();
md.Destroy();


Se tudo correu bem, ou seja, o utilizador selecionou um site da combobox1 e carregou no botão para carregar a lista, e este processo concluiu sem erros, devemos então percorrer os nós do XML para adicionar à combobox2 os títulos dos artigos, além disso guardamos em duas listas o texto resumo do artigo e o link para este.
Assim 
//seleciona o primeiro item
XmlNodeList items = doc.SelectNodes("//item");
//Lista de items
foreach (XmlNode item in items){
//titulo
store.AppendValues(item.SelectSingleNode("title").InnerText);
//descricao
//algumas descriçoes tem tags html mas vamos tratar delas
int x=item.SelectSingleNode("description").InnerText.IndexOf("<");
if (x>0)
textos.Add( item.SelectSingleNode("description").InnerText.Substring(0,x));
else
textos.Add( item.SelectSingleNode("description").InnerText);
//link
urls.Add(item.SelectSingleNode("link").InnerText);        
}

Destaco o pormenor de que alguns textos têm links que não têm boa aparência no label assim só adicionamos ao label1 o texto até encontrarmos um <.

Para mostrar o texto do artigo controlamos o evento change da combobox2

protected void OnCombobox2Changed (object sender, System.EventArgs e)
{
if(this.combobox2.Active<0) return;
this.label1.Text=textos[this.combobox2.Active];
}

Depois disto tudo falta somente o clique no botão para ler o artigo

protected void OnButton3Clicked (object sender, System.EventArgs e)
{
string temp;
if (this.combobox2.Active<0) return;
temp=urls[this.combobox2.Active];
System.Diagnostics.Process.Start(temp);
}

Assim está tudo.

sexta-feira, 2 de março de 2012

XML

XML - Extensible Markup Language

Segundo a Wikipédia "O XML é um formato para a criação de documentos com dados organizados de forma hierárquica...".
Ainda segundo a Wikipédia "Seu propósito principal é a facilidade de compartilhamento de informações através da internet."
Em última análise são ficheiros de texto que incluem tags que descrevem a estrutura dos dados a par da informação, ainda que não inclua o tipo de dados de cada campo.
As RSS feeds são uma das utilizações do formato XML.
Em relação às RSS a Wikipédia diz "Os feeds RSS oferecem conteúdo Web ou resumos de conteúdo juntamente com os links para as versões completas deste conteúdo e outros metadados. Esta informação é entregue como um arquivo XML chamado "RSS feed", "webfeed", "Atom" ou ainda canal RSS."
Com estas informações podemos construir um leitor de feeds RSS, para este exemplo vou utilizar PHP para ler uma feed RSS a partir do site da PCMagazine.



Começamos por criar a estrutura do código PHP:


<!DOCTYPE html>
<html>
<head>
<title>Leitor de Feeds RSS</title>
</head>
<body>
<h1>PC Magazine</h1>
<ul>
                           <!--Inserir aqui o código-->
</ul>

</body>
</html>

Agora na parte <body> do documento criamos uma lista com os artigos. Primeiro carregamos o ficheiro XML

<?PHP
$dom = simplexml_load_file("http://rssnewsapps.ziffdavis.com/pcmag.xml");

De seguida fazemos um ciclo que percorre os items (artigos) publicados na feed:

foreach ($dom->channel->item as $item)
{
print "<li>";
print "<a href='{$item->link}'>";
print $item->title;
print "</a>";
print "</li>";
}
?>
Para cada artigo mostramos o titulo e inserimos um link para o texto no site da pcmagazine.
E pronto o código no final fica assim:


<!DOCTYPE html>
<html>
<head>
<title>Leitor de Feeds RSS</title>
</head>
<body>
<h1>PC Magazine</h1>
<ul>
<?PHP
$dom = simplexml_load_file("http://rssnewsapps.ziffdavis.com/pcmag.xml");
foreach ($dom->channel->item as $item)
{
print "<li>";
print "<a href='{$item->link}'>";
print $item->title;
print "</a>";
print "</li>";
}
?>
</ul>
</body>
</html>


Simples como só o PHP sabe ser.