Downloads do blog via 4Shared

Deixe um comentário

Há algum tempo o 4Shared está exigindo que os usuários façam login para baixar os arquivos, e acho que por isso alguns visitantes do meu site não estão conseguindo baixar os arquivos que disponibilizo.

Disponibilizei novamente todos os arquivos do meu blog no DropBox. Veja abaixo a lista de artigos com opção para download e os novos links.

1. Dica: Código Hexadecimal das Cores

2. Exemplo de Implementação de IDisposable

3. Entity Framework: Mudando a ConnectionString de um ObjectContext em runtime

4. Extension Methods no C#

5. LINQ Extensions

6. LINQ e Lambda Expression: OrderBy em vários campos

7. String Vs. StringBuilder

8. Entity Framework + TransactionScope

9. Programando Threads em C# e VB.NET

10. Passar parâmetros para Threads

11. Dica: Usar o WCF Test Client sem Visual Studio instalado

 

Anúncios

Exemplo de Implementação de IDisposable

3 comentários

A interface IDisposable é usada para liberar recursos. O garbage collector (coletor de lixo) libera automaticamente a memória alocada para um objeto gerenciado quando este objeto não estiver mais sendo utilizado. No entanto, não é possível prever quando esta coleta de lixo irá ocorrer. Além disso, o garbage collector não tem conhecimento dos recursos não gerenciados.

Usamos o método Dispose desta interface para liberar explicitamente recursos não gerenciados em conjunto com o garbage collector. O consumidor de um objeto pode chamar esse método quando o objeto não for mais necessário.

Inicialmente, precisamos fazer a classe que queremos implementar IDisposable herdar desta interface.

// C#
public class GerenciarClientes : IDisposable
{
    public void Dispose()
    {

    }
}

 

' VB.NET
Public Class GerenciarClientes
    Implements IDisposable

    Public Overridable Sub Dispose() Implements IDisposable.Dispose

    End Sub
End Class

OBS: Em C#, para que o próprio Visual Studio implemente o método Dispose, basta clicar com o botão direito do mouse sobre a interface, escolher a opção ‘Implement Interface’ e ‘Implement Interface’ novamente. Esse mesmo procedimento funciona com a implementação de qualquer interface em uma classe.

Para controlarmos corretamente a liberação dos recursos, é necessário criar uma variável do tipo booleana, que indicará se o método Dispose já foi disparado.

Além disso, precisamos criar um método privado que será chamado de lugares distintos e controlará quais recursos (gerenciados ou não) devem ser liberados.

// C#

// booleano para controlar se
// o método Dispose já foi chamado
bool disposed = false;

// método privado para controle
// da liberação dos recursos
private void Dispose(bool disposing)
{
    // Verifique se Dispose já foi chamado.
    if (!this.disposed)
    {
        if (disposing)
        {
            // Liberando recursos gerenciados

        }

        // Seta a variável booleana para true,
        // indicando que os recursos já foram liberados
        disposed = true;
    }
}

 

' VB.NET

' booleano para controlar se
' o método Dispose já foi chamado
Private disposed As Boolean = False

' método privado para controle
' da liberação dos recursos
Private Sub Dispose(ByVal disposing As Boolean)
    ' Verifique se Dispose já foi chamado.
    If Not Me.disposed Then
        If disposing Then
            ' Liberando recursos gerenciados

        End If

        ' Seta a variável booleana para true,
        ' indicando que os recursos já foram liberados
        disposed = True
    End If
End Sub

Dentro do método público Dispose, é necessário chamar o método privado Dispose e também informar o garbage collector para não chamar o método finalizador da classe, pois estamos controlando isso manualmente. Para isto, utilizamos o método GC.SuppressFinalize().

// C#
bool disposed = false;

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

 

' VB.NET
Dim disposed As Boolean = False

Protected Overridable Sub Dispose() Implements IDisposable.Dispose
    Dispose(True)
    GC.SuppressFinalize(Me)
End Sub

Por fim, precisamos programar o método finalizador da classe, que é invocado pelo garbage collector para liberar os recursos. Toda liberacao implementada dentro dele só será executada quando o metodo for invocado, porém não conseguimos saber quando isto vai acontecer e recursos importantes podem ficar presos até que isto aconteça.

// C#
~GerenciarClientes()
{
    Dispose(false);
}
' VB.NET
Protected Overrides Sub Finalize()
    Dispose(False)
    MyBase.Finalize()
End Sub

Pronto, implementamos IDisposable. Com essa implementação, sempre que tivermos uma instância da classe que não será mais utilizada, devemos chamar o método Dispose ou envolvê-la em um bloco using.

// C#

// forma simples
GerenciarClientes g1 = new GerenciarClientes();

// código consumidor da instância

// Liberando o recurso
g1.Dispose();

// usando try...finally
GerenciarClientes g2 = new GerenciarClientes();
try
{
    // código consumidor da instância
}
finally
{
    g2.Dispose();
}

// ou com using
using (GerenciarClientes g3 = new GerenciarClientes())
{
    // código consumidor da instância
} // aqui o método Dispose é invocado

 

' VB.NET

' forma simples
Dim g1 As New GerenciarClientes()
' código consumidor da instância

' Liberando o recurso
g1.Dispose()

' usando Try...Finally
Dim g2 As New GerenciarClientes()
Try
    ' código consumidor da instância
Finally
    g2.Dispose()
End Try

' ou com using
Using g3 As New GerenciarClientes()
    ' código consumidor da instância
End Using ' aqui o método Dispose é invocado


Referências

Código Fonte

O código fonte deste post pode ser baixado aqui.



Obrigado

Passar parâmetros para Threads

3 comentários

Há algum tempo, escrevi um post que mostra formas de programar threads em C# e VB.NET. Na ocasião, mostrei uma forma de passar parâmetros para uma thread. O intuito deste post é mostrar uma outra forma existente para este mesmo objetivo.

Primeiro, criamos uma classe que conterá propriedades que representarão os parâmetros que devem ser passados para a Thread.

// C#
public class DadosLog
{
    public Exception Exception { get; set; }
    public string StrNomeProjeto { get; set; }
    public string StrNomeClasse { get; set; }
    public string StrNomeMetodo { get; set; }
}
' VB.NET
Public Class DadosLog
    Public Property Exception() As Exception
        Get
            Return _Exception
        End Get
        Set(ByVal value As Exception)
            _Exception = value
        End Set
    End Property
    Private _Exception As Exception
    Public Property StrNomeProjeto() As String
        Get
            Return _StrNomeProjeto
        End Get
        Set(ByVal value As String)
            _StrNomeProjeto = value
        End Set
    End Property
    Private _StrNomeProjeto As String
    Public Property StrNomeClasse() As String
        Get
            Return _StrNomeClasse
        End Get
        Set(ByVal value As String)
            _StrNomeClasse = value
        End Set
    End Property
    Private _StrNomeClasse As String
    Public Property StrNomeMetodo() As String
        Get
            Return _StrNomeMetodo
        End Get
        Set(ByVal value As String)
            _StrNomeMetodo = value
        End Set
    End Property
    Private _StrNomeMetodo As String
End Class

Em seguida, o método que executará o processo em segundo plano deve ter como parâmetro de entrada um object.

// C#
public void ExecutarLogErro(object _dadosLog)
{
    DadosLog dadosLog = (_dadosLog as DadosLog);

    // Gravar Log no Repositório ou no Log de Eventos do Windows
}
' VB.NET
Public Sub ExecutarLogErro(ByVal _dadosLog As Object)
    Dim dadosLog As DadosLog = TryCast(_dadosLog, DadosLog)

    ' Gravar Log no Repositório ou no Log de Eventos do Windows
End Sub

Para concluir, passamos a instância da classe DadosLog no método Start() no momento da chamada à thread.

// C#
public void LogarErro(Exception exception,
    string strNomeProjeto,
    string strNomeClasse,
    string strNomeMetodo)
{
    DadosLog dadosLog = new DadosLog()
    {
        Exception = exception,
        StrNomeClasse = strNomeClasse,
        StrNomeMetodo = strNomeMetodo,
        StrNomeProjeto = strNomeProjeto
    };

    GerenciaLogErro gle = new GerenciaLogErro();
    Thread t = new Thread(gle.ExecutarLogErro);
    t.IsBackground = true;
    t.Start(dadosLog);
}
' VB.NET
Public Sub LogarErro(ByVal exception As Exception, _
                     ByVal strNomeProjeto As String, _
                     ByVal strNomeClasse As String, _
                     ByVal strNomeMetodo As String)
    Dim dadosLog As New DadosLog() With { _
     .Exception = exception, _
     .StrNomeClasse = strNomeClasse, _
     .StrNomeMetodo = strNomeMetodo, _
     .StrNomeProjeto = strNomeProjeto _
    }

    Dim gle As New GerenciaLogErro()
    Dim t As New Thread(AddressOf gle.ExecutarLogErro)
    t.IsBackground = True
    t.Start(dadosLog)
End Sub

Essa é apenas mais uma maneira de passar informações para a Thread. Existem outras e mais para frente tentarei demonstrar aqui.

OBS: Este código fonte pode ser baixado aqui.

Obrigado.

Entity Framework + TransactionScope

8 comentários

Antes de unir os dois assuntos, falarei um pouco de cada um para entendermos melhor a ideia deste artigo.

TransactionScope

Presente no .Net Framework desde a versão 2.0, TransactionScope fica disponível após adicionar referencia para System.Transactions, e fornece uma maneira simples de marcar um bloco de código como participante em uma transação, sem a necessidade de interagir com a transação em si. Um TransactionScope pode gerenciar o ambiente da transação automaticamente, inclusive transações de bancos de dados, como o Sql Server. A sintaxe de utilização é algo como isto:

// C#
using (TransactionScope scope = new TransactionScope())
{
    // Código que participa da transação

    scope.Complete();
} // Commit ou RollBack
' VB.NET
Using scope As New TransactionScope()
    ' Código que participa da transação

    scope.Complete()
End Using ' Commit ou RollBack

Mais detalhes sobre TransactionScope:
Classe TransactionScope
Enumerador TransactionScopeOption


Entity Framework

Acho que aqui não é necessário definir ou conceituar o Entity Framework, e sim, expor os motivos de usar o TransactionScope junto a ele. Se tiver interesse em ler conceitos sobre Entity Framework, clique aqui, aqui ou aqui.

Trabalhar com Entity Framework e um banco de dados de tamanho pequeno é simples, pois criamos o Modelo Objeto-Relacional (MOR) e já podemos sair desenvolvendo.  Quando temos um banco de dados muito grande, se colocarmos tudo em um único modelo, podemos ter diversos problemas, entre eles, desempenho e dificuldade em organização. Em geral, é recomendado ter modelos com até 100 entidades. Passando disso, já devemos começar a pensar em dividir em vários modelos.

NOTA: Utilizando apenas um modelo, todas as atualizações feitas através do Entity Framework já acontecem em uma transação, ou seja, se ocorrer algum erro antes que o método SaveChanges() esteja concluído, o RollBack é garantido. Neste caso, não é necessário usar uma transação explícita.

Aí vem a pergunta: E para controlar transações usando mais de um modelo? O intuído de escrever esse artigo é justamente estudar um pouco mais este assunto.


Transações usando mais de um Modelo Objeto-Relacional

Uma das maneiras de termos a mesma transação para mais de um modelo é usarmos o TransactionScope. A aplicação é simples e muito útil. Melhor do que escrever, é ir direto para o código.

// C#
using System;
using System.Transactions;
using EntityTransactionCS.Model1;
using EntityTransactionCS.Model2;

namespace EntityTransactionCS
{
    class Program
    {
        static void Main(string[] args)
        {
            bool bolSucesso = false;

            // Instanciando o Modelo1
            Modelo1 context1 = new Modelo1();

            // Instanciando o Modelo2
            Modelo2 context2 = new Modelo2();

            try
            {
                // Envolvendo o processo por um TransactionScope
                using (TransactionScope scope = new TransactionScope())
                {
                    // Inserindo Cliente com o Modelo1
                    Model1.CLIENTES cliente = new Model1.CLIENTES()
                    {
                        NM_CLIENTE = "Fernando Ottoboni",
                        NR_IDADE = 28
                    };

                    //context1.AddToCLIENTES(cliente);
                    context1.CLIENTES.AddObject(cliente);

                    // Inserindo Produto com o Modelo2
                    Model2.PRODUTOS produto = new Model2.PRODUTOS()
                    {
                        NM_PRODUTO = "Notebook DELL Inspiron 1564 4G 320GB.",
                        VL_PRECO = 2699
                    };
                    //context2.AddToPRODUTOS(produto);
                    context2.PRODUTOS.AddObject(produto);

                    try
                    {
                        // Salva as alterações realizadas no Contexto
                        context1.SaveChanges();
                        context2.SaveChanges();

                        bolSucesso = true;
                    }
                    catch
                    {
                        bolSucesso = false;
                    }

                    if (bolSucesso)
                    {
                        // Completa a transação (Fará o Commit)
                        scope.Complete();
                    }

                    // Somente depois de passar pelo Complete() que o
                    // TransactionScope fará o Commit.
                    // Caso contrário, o RollBack será executado.

                } // Commit ou RollBack

                if (bolSucesso)
                    Console.WriteLine("Dados gravados.");
                else
                    Console.WriteLine("Problemas ao gravar.");
            }
            finally
            {
                context1.Dispose();
                context2.Dispose();
            }

            Console.ReadKey();
        }
    }
}
' VB.NET
Imports EntityTransactionVB.EntityTransactionVB
Imports EntityTransactionVB.EntityTransactionVB.Model1
Imports EntityTransactionVB.EntityTransactionVB.Model2
Imports System.Transactions

Module Module1

    Sub Main()
        Dim bolSucesso As Boolean = False

        ' Instanciando o Modelo1
        Dim context1 As New Modelo1()

        ' Instanciando o Modelo2
        Dim context2 As New Modelo2()

        Try
            ' Envolvendo o processo por um TransactionScope
            Using scope As New TransactionScope()

                ' Inserindo Cliente com o Modelo1
                Dim cliente As New Model1.CLIENTES() With { _
                    .NM_CLIENTE = "Fernando Ottoboni", _
                    .NR_IDADE = 28 _
                }
                'context1.AddToCLIENTES(cliente)
                context1.CLIENTES.AddObject(cliente)

                ' Inserindo Produto com o Modelo2
                Dim produto As New Model2.PRODUTOS() With {
                    .NM_PRODUTO = "Notebook DELL Inspiron 1564 4G 320GB.", _
                    .VL_PRECO = 2699 _
                }
                'context2.AddToPRODUTOS(produto)
                context2.PRODUTOS.AddObject(produto)

                Try
                    ' Salva as alterações realizadas no Contexto
                    context1.SaveChanges()
                    context2.SaveChanges()

                    bolSucesso = True
                Catch
                    bolSucesso = False
                End Try

                If bolSucesso Then
                    ' Completa a transação (Fará o Commit)
                    scope.Complete()
                End If

                ' Somente depois de passar pelo Complete() que o
                ' TransactionScope fará o Commit.
                ' Caso contrário, o RollBack será executado.

            End Using ' Commit ou RollBack

            If bolSucesso Then
                Console.WriteLine("Dados gravados.")
            Else
                Console.WriteLine("Problemas ao gravar.")
            End If
        Finally
            context1.Dispose()
            context2.Dispose()
        End Try

        Console.ReadKey()
    End Sub

End Module


NOTA 1:Para excluir da transação uma determinada camada do código, utilize TransactionScopeOption.Supress na chamada ao construtor da classe TransactionScope.

// C#
using (TransactionScope scope =
    new TransactionScope(TransactionScopeOption.Suppress))
{
    // Código a ser suprimido
}
' VB.NET
Using scope As _
    New TransactionScope(TransactionScopeOption.Suppress)
    ' Código a ser suprimido
End Using


NOTA 2: TransactionScope pode ser utilizada com vários modelos de acesso a dados, inclusive providers que não são distribuídos pela Microsoft. Veja um exemplo utilizando SqlConnection:

// C#
using (TransactionScope scope = new TransactionScope())
{
    using (SqlConnection connection1 = new SqlConnection(connectString1))
    {
        connection1.Open();

        // Operações com connection1

        using (SqlConnection connection2 = new SqlConnection(connectString2))
        {
            connection2.Open();

            // Operações com connection2
        }
    }

    scope.Complete();
}
' VB.NET
Using scope As New TransactionScope()
    Using connection1 As New SqlConnection(connectString1)
        connection1.Open()

        ' Operações com connection1

        Using connection2 As New SqlConnection(connectString2)
            connection2.Open()

            ' Operações com connection2
        End Using
    End Using

    scope.Complete()
End Using



Conclusão

É de suma importância (e também uma boa prática) controlarmos transações em nossa aplicações. A classe TransactionScope nos fornece as ferramentas idais para isso e, quando aplicada em situações em que temos que quebrar o Modelo Objeto-Relacional e várias partes, facilita muito a nossa vida e dá todo o suporte que precisamos para manipulação de transações.


O download do código fonte pode ser feito aqui.


Obrigado

Dica: Postar Código Fonte no WordPress

1 Comentário

Hoje aprendi como destacar os códigos fonte publicados aqui no WordPress. Quem me deu a dica foi o Vitão, depois dei uma pesquisadinha e achei um link do próprio WordPress sobre isso.

A grosso modo, a forma de destacar o código é essa:











 
Os códigos postados  ficam dessa maneira:
// C#
private void ExemploDeCodigo(int primeiroParametro, string segundoParametro)
{
    // Implementação do método
}
' VB.NET
Private Sub ExemploDeCodigo(primeiroParametro As Integer, segundoParametro As String)
	' Implementação do método
End Sub

Para mais detalhes, basta acessar este link.


Obrigado

Código fonte do artigo “Programando Threads em C# e VB.NET”

Deixe um comentário

Há alguns dias publiquei um artigo intitulado Programando Threads em C# e VB.NET. Algumas pessoas me pediram o código fonte da solução que criei para demonstração. Consegui postar o código fonte apenas hoje.

Download através do 4Shared neste link. (OBS: Arquivo compactado no formato ZIP)

Tutorial de utilização do 4Shared neste link.

OBS: Há algum tempo o 4Shared está exigindo que o usuário esteja logado para fazer download de arquivo, por isso passei a postar os arquivos no DropBox. Caso não consiga fazer o download no 4Shared, utilize este link do DropBox para baixar o arquivo.

Requisitos: Visual Studio 2010 e .Net Framework 4.

Obrigado.

Programando Threads em C# e VB.NET

7 comentários

Conceitos

Todo programa desenvolvido em C# ou VB.NET possue uma thread. Esta é conhecida como thread principal. Muitos programas geralmente precisam realizar tarefas que levam um longo tempo. Se a thread principal do aplicativo for dedicada a isto, o aplicativo pode parar de responder até que a execução esteja concluída.

Para permitir que um aplicativo execute uma tarefa e continue a responder, ou para realizar várias tarefas ao mesmo tempo, podemos programar conceitos de multitarefas (multithreading).

Além de realizar processamento em segundo plano, é uma ótima maneira de melhorar o desempenho das tarefas do processador em computadores com vários processadores ou núcleos. Se um computador tem vários processadores ou vários núcleos em um único processador, ele pode executar múltiplas tarefas simultaneamente, mesmo que elas exijam tempo de processamento.

Portanto, acrescentar multitarefa em uma aplicação pode reduzir o tempo que leva para completar uma tarefa.

O intuito deste artigo é demonstrar algumas formas de implementar Threads, desde chamadas simples a uma thread, passagem e recebimento de parâmetros, etc.

 

Criando a primeira thread

Existem várias formas de programarmos execução em segundo plano nos softwares que desenvolvemos. Uma delas é criar um método dentro da classe exclusivo para execução da tarefa em segundo plano e outro responsável pela chamada desta tarefa.

// C#
public class GerenciaThread
{
    public void ChamarThread()
    {
        System.Threading.ThreadStart ts =
            new System.Threading.ThreadStart(ExecutarThread);
        System.Threading.Thread t =
            new System.Threading.Thread(ts);
        t.IsBackground = true;
        t.Start();
    }

    private void ExecutarThread()
    {
        // Código a ser executado pela thread
    }
}
' VB.NET
Public Class GerenciaThread
    Public Sub ChamarThread()
        Dim ts As New _
           System.Threading.ThreadStart(AddressOf ExecutarThread)
        Dim t As New System.Threading.Thread(ts)
        t.IsBackground = True
        t.Start()
    End Sub

    Private Sub ExecutarThread()
        ' Código a ser executado pela thread
    End Sub
End Class

 
Repare que no exemplo acima, o único método público dentro da classe ‘GerenciaThread’ é o ‘ChamarThread’. Coloquei desta forma para evitar que o método ‘ExecutarThread’ seja chamado em outros locais a não ser nesta mesma classe, e ,desta forma, permitir que o processo seja executado apenas através da thread em background.

Para iniciar o processamento em segundo plano, basta chamar o método ‘ChamarThread’.

// C#
GerenciaThread gt = new GerenciaThread();
gt.ChamarThread();
' VB.NET
Dim gt As New GerenciaThread()
gt.ChamarThread()

 
NOTA: Para mais informações sobre System.Threading, acesse este endereço.

 

Passando dados para a Thread

Em algumas situações é necessário passar informações para a thread para serem usadas durante o processamento. Essa passagem de dados pode ser feita de várias maneiras. Uma delas é criar um construtor na classe que executa a thread permitindo um ou mais parâmetros. Ao criar a instância da classe, use este construtor e passe os parâmetros desejados. Por exemplo, podemos executar Logs de erros em threads em background. Mas para que o log seja armazenado em algum repositório, é necessário informar para a thread algumas informações como o erro ocorrido, onde ele ocorreu, etc. Usando o mesmo conceito do exemplo anterior, podemos colocar no construtor da classe ‘GerenciaThread’ os parâmetros necessários para gravação do log. Algo parecido com isso:

// C#
public class GerenciaLogErro
{
    private Exception exception;

    // Nome do projeto onde ocorreu o erro
    private string strNomeProjeto;

    // Nome da classe onde ocorreu o erro
    private string strNomeClasse;

    // Nome do método onde ocorreu o erro
    private string strNomeMetodo;

    public GerenciaLogErro(Exception _exception,
        string _projeto,
        string _classe,
        string _metodo)
    {
        this.exception = _exception;
        this.strNomeProjeto = _projeto;
        this.strNomeClasse = _classe;
        this.strNomeMetodo = _metodo;
    }

    public void ExecutarLogErro()
    {
        // Gravar Log no Repositório
        // ou no Log de Eventos do Windows
    }
}

public class LogErro
{
    public void LogarErro(Exception exception,
        string strNomeProjeto,
        string strNomeClasse,
        string strNomeMetodo)
    {
        GerenciaLogErro gle = new
            GerenciaLogErro(exception,
            strNomeProjeto,
            strNomeClasse,
            strNomeMetodo);

        System.Threading.ThreadStart ts = new
            System.Threading.ThreadStart(gle.ExecutarLogErro);
        System.Threading.Thread t = new
            System.Threading.Thread(ts);
        t.IsBackground = true;
        t.Start();
    }
}
' VB.NET
Public Class GerenciaLogErro
    Private exception As Exception

    ' Nome do projeto onde ocorreu o erro
    Private strNomeProjeto As String

    ' Nome da classe onde ocorreu o erro
    Private strNomeClasse As String

    ' Nome do método onde ocorreu o erro
    Private strNomeMetodo As String

    Public Sub New(ByVal _exception As Exception,
        ByVal _projeto As String,
        ByVal _classe As String,
        ByVal _metodo As String)
        Me.exception = _exception
        Me.strNomeProjeto = _projeto
        Me.strNomeClasse = _classe
        Me.strNomeMetodo = _metodo
    End Sub

    Public Sub ExecutarLogErro()
        ' Gravar Log no Repositório
        ' ou no Log de Eventos do Windows
    End Sub
End Class

Public Class LogErro
    Public Sub LogarErro(ByVal exception As Exception,
        ByVal strNomeProjeto As String,
        ByVal strNomeClasse As String,
        ByVal strNomeMetodo As String)
        Dim gle As New  _
            GerenciaLogErro(exception, _
            strNomeProjeto, _
            strNomeClasse, _
            strNomeMetodo)
        Dim ts As New _
            System.Threading.ThreadStart(AddressOf _
            gle.ExecutarLogErro)
        Dim t As New System.Threading.Thread(ts)
        t.IsBackground = True
        t.Start()
    End Sub
End Class

 
Repare que na classe ‘GerenciaLogErro’ foi criado um construtor que recebe 4 parâmetros, e esses parâmetros serão gravados no repositório para registro do Log.

NOTA: Uma prática comum com relação a gravação de Logs é utilizar o EventLog do Windows. Se desejar mais detalhes sobre isso, consulte este e este links.

A chamada para o método que executa o log pode ser feita conforme o código a seguir.

// C#
try
{
    //Coloque aqui o código que deverá ser executado no método
    Console.WriteLine("Esta é a Thread Principal");
}
catch (Exception ex)
{
    LogErro logErro = new LogErro();
    logErro.LogarErro(ex,
        "ThreadPassingData",
        "Program", "Main");
}
' VB.NET
Try
    'Coloque aqui o código que deverá ser executado no método
    Console.WriteLine("Esta é a Thread Principal")
Catch ex As Exception
    Dim logErro As New LogErro()
    logErro.LogarErro(ex, _
        "ThreadPassingData", _
        "Program", "Main")
End Try

 

Recebendo retorno da Thread

Além da passagem de dados para a Thread, existem situações onde é necessário retornar informações quando a execução da thread estiver concluída. Para recuperar dados da thread, devemos criar um método que aceita os resultados de retorno como parâmetro. Em seguida, crie um delegate para este método. O construtor da classe deve aceitar um delegate que representa o método call-back. Antes que a thread esteja concluída, ele chama o delegate de retorno. O código a seguir demonstra isso.

// C#
class Program
{
    static void Main(string[] args)
    {
        // Recebendo entradas do usuário
        Console.WriteLine();
        Console.Write("Informe o primeiro valor: ");
        double d1 = Convert.ToDouble(Console.ReadLine());

        Console.WriteLine();
        Console.Write("Informe o segundo valor: ");
        double d2 = Convert.ToDouble(Console.ReadLine());

        Console.WriteLine();
        Console.WriteLine("OPERAÇÕES");
        Console.WriteLine("1 - Soma");
        Console.WriteLine("2 - Subtração");
        Console.WriteLine("3 - Multiplicação");
        Console.WriteLine("4 - Divisão");
        Console.Write("Informe a operação desejada: ");
        Operacao op =
            (Operacao)Convert.ToInt32(Console.ReadLine());

        // Instância da classe Calculadora
        Calculadora calc = new
            Calculadora(d1, d2, op,
            new ResultDelegate(ResultCallback));

        // Criando a thread
        System.Threading.ThreadStart ts = new
            System.Threading.ThreadStart(calc.Calcular);
        System.Threading.Thread t = new
            System.Threading.Thread(ts);

        // Iniciando a execução da thread
        t.Start();

        Console.WriteLine();
        Console.WriteLine("Thread principal em execução.");
        t.Join();
        Console.WriteLine();
        Console.WriteLine("Processamento concluído.");
        Console.ReadKey();
    }

    public static void ResultCallback(double valor)
    {
        Console.WriteLine("Valor Retornado da Calculadora: {0}",
            valor);
    }
}

public class Calculadora
{
    // Valores utilizados durante o cálculo.
    private double valor1;
    private double valor2;
    private Operacao operacao;

    // Delegate usado para executar o método de
    // call-back quando a thread estiver completa
    private ResultDelegate callback;

    // O construtor obtendo os parâmetros
    public Calculadora(double _valor1,
        double _valor2,
        Operacao _operacao,
        ResultDelegate _callback)
    {
        this.valor1 = _valor1;
        this.valor2 = _valor2;
        this.operacao = _operacao;
        callback = _callback;
    }

    public void Calcular()
    {
        double valorResultado;

        if (this.operacao == Operacao.Soma)
            valorResultado = this.valor1 + this.valor2;
        else if (this.operacao == Operacao.Subtracao)
            valorResultado = this.valor1 - this.valor2;
        else if (this.operacao == Operacao.Multiplicacao)
            valorResultado = this.valor1 * this.valor2;
        else if (this.operacao == Operacao.Divisao)
            valorResultado = this.valor1 / this.valor2;
        else
            valorResultado = 0;

        if (callback != null)
            callback(valorResultado);
    }
}

// Delegate que define a assinatura
//para o método de callback.
public delegate void ResultDelegate(double valor);

// Enumerador de operações
public enum Operacao
{
    Soma = 1,
    Subtracao = 2,
    Multiplicacao = 3,
    Divisao = 4
}
' VB.NET
Sub Main()
    ' Recebendo entradas do usuário
    Console.WriteLine()
    Console.Write("Informe o primeiro valor: ")
    Dim d1 As Double = Convert.ToDouble(Console.ReadLine())

    Console.WriteLine()
    Console.Write("Informe o segundo valor: ")
    Dim d2 As Double = Convert.ToDouble(Console.ReadLine())

    Console.WriteLine()
    Console.WriteLine("OPERAÇÕES")
    Console.WriteLine("1 - Soma")
    Console.WriteLine("2 - Subtração")
    Console.WriteLine("3 - Multiplicação")
    Console.WriteLine("4 - Divisão")
    Console.Write("Informe a operação desejada: ")
    Dim op As Operacao = _
        DirectCast(Convert.ToInt32(Console.ReadLine()), Operacao)

    ' Instância da classe Calculadora
    Dim calc As New _
        Calculadora(d1, d2, op, New _
        ResultDelegate(AddressOf ResultCallback))

    ' Criando a thread
    Dim ts As New _
        System.Threading.ThreadStart(AddressOf calc.Calcular)
    Dim t As New _
        System.Threading.Thread(ts)

    ' Iniciando a execução da thread
    t.Start()

    Console.WriteLine()
    Console.WriteLine("Thread principal em execução.")
    t.Join()
    Console.WriteLine()
    Console.WriteLine("Processamento concluído. ")
    Console.ReadKey()
End Sub

Public Sub ResultCallback(ByVal valor As Double)
    Console.WriteLine("Valor Retornado da Calculadora: {0}", _
        valor)
End Sub

Public Class Calculadora
    ' Valores utilizados durante o cálculo.
    Private valor1 As Double
    Private valor2 As Double
    Private operacao As Operacao

    ' Delegate usado para executar o método de
    ' call-back quando a thread estiver completa
    Private callback As ResultDelegate

    ' O construtor obtendo os parâmetros
    Public Sub New(ByVal _valor1 As Double,
        ByVal _valor2 As Double,
        ByVal _operacao As Operacao,
        ByVal _callback As ResultDelegate)
        Me.valor1 = _valor1
        Me.valor2 = _valor2
        Me.operacao = _operacao
        callback = _callback
    End Sub

    Public Sub Calcular()
        Dim valorResultado As Double

        If Me.operacao = operacao.Soma Then
            valorResultado = Me.valor1 + Me.valor2
        ElseIf Me.operacao = operacao.Subtracao Then
            valorResultado = Me.valor1 - Me.valor2
        ElseIf Me.operacao = operacao.Multiplicacao Then
            valorResultado = Me.valor1 * Me.valor2
        ElseIf Me.operacao = operacao.Divisao Then
            valorResultado = Me.valor1 / Me.valor2
        Else
            valorResultado = 0
        End If

        If (callback <> Nothing) Then
            callback(valorResultado)
        End If
    End Sub
End Class

' Delegate que define a assinatura para o método de callback.
Public Delegate Sub ResultDelegate(ByVal valor As Double)

' Enumerador de operações
Public Enum Operacao
    Soma = 1
    Subtracao = 2
    Multiplicacao = 3
    Divisao = 4
End Enum

 
O resultado desta execução deverá ser algo como isto:

Informe o primeiro valor: 10

Informe o segundo valor: 20

OPERAÇOES
1 – Soma
2 – Subtraçao
3 – Multiplicaçao
4 – Divisao
Informe a operaçao desejada: 2

Thread principal em execuçao.
Valor Retornado da Calculadora: -10

Processamento concluído.

NOTA: Fique sempre atento para o ocorrência de deadlocks. Sempre que necessário, utilize locks nos locais onde a concorrência pode ser grande e causar deadlocks. Isso é assunto para um outro post, mas por enquanto, se quiser saber mais sobre isso, veja este link.

Conclusão

Usar Threads é sempre vantajoso no ponto de vista de que podemos aproveitar o poder de processamento dos processadores que possuem mais de um núcleo, pois podemos programar para que cada thread tenha o seu processamento realizado por núcleos diferentes. Mas é sempre importante avaliar corretamente a utilização de threads pois a concorrência poderá causar deadlocks e funcionamento incorreto de um procedimento.

Estude mais

Escrevi outros artigos sobre este mesmo assunto. Caso queira acessá-los, clique nos links abaixo:

Referências

Older Entries