VB .NET - Executando um método periodicamente


Algum dia você pode precisar ter que executar um método de forma periódica em intervalos regulares de tempo. Tarefas como copiar um arquivo, enviar uma mensagem, limpar o cache, etc. Você saberia como fazer isso ?

A classse System.Threading.Timer permite realizar esta tarefa através da execução de um método referenciado pelo delegate TimerCallBack em intervalos pré-definidos. O método referenciado é executado no contexto de uma thread a partir do pool de threads.

A classe Timer do namespace System.Threading fornece um mecanismo para executar um método a intervalos especificados.

Você deve usar um delegate TimerCallback para especificar o método que você deseja que o Timer execute. Este delegate é especificado quando o timer é construído, e não pode ser alterado. O método não será executado na thread que criou o timer, mas será executado em uma thread fornecida pelo pool de threads (ThreadPool) fornecido pelo sistema.

Obs: O delegate TimerCallback representa o método que lida com chamadas de um Timer.

Quando você cria um objeto Timer, define dois intervalos de tempo:

Uma vez criado o objeto Timer , você pode modificar o intervalo usado pelo Timer via método Change, mas não pode alterar o método que foi chamado.

Depois de terminar com o objeto Timer, você deverá chamar o seu método Dispose para liberar os recursos dos sistema. Fazendo isso você cancela qualquer método que estiver agendado para execução.

Vejamos a seguir um exemplo mostrando como usar este recurso:

Vou usar o Visual Basic 2008 Express Edition e criar uma aplicação console que mostra como usar o objeto Timer para chamar o método gerenciaTimer de forma periódica.

Inicialmente o objeto Timer será configurado para chamar o método timerAcionado depois de 5 segundos e então passará a chamá-lo a intervalo de 2 segundos. No exemplo você também pode informar um novo intervalo de tempo via console.

Abra o Visual Basic 2008 Express Edition e crie uma aplicação do tipo Console com o nome de executaMetodoPeriodicamente;

A seguir inclua o seguinte código no modulo:

Imports System
Imports System.Threading
Namespace Macoratti

    Class usando_Timer

        Public Shared Sub Main()

            ' Cria um objeto estado que é passado para o método timerAcionado
            ' quando ele é disparado para exibir uma mensagem no console
            Dim estado As String = "Timer disparado."
            Console.WriteLine("{0} : Criando o Timer.", DateTime.Now.ToString("HH:mm:ss.ffff"))

            ' Cria o timer que dispara primeiro depois de 5 segundos e depois a cada 2 segundos
            ' O objeto threadTimer é liberado automaticcamente no final do bloco using
            Using threadTimer As New Timer(AddressOf timerAcionado, estado, 5000, 2000)
                Dim periodo As Integer
                ' Le um novo intervalo de tempo fornecido a partir do console
                ' até que o usuário informe 0 (zero). Para valores inválidos será usado
                ' o valor padrão de zero que irá encerrar o programa
                Do
                    Try
                        periodo = Int32.Parse(Console.ReadLine())
                    Catch ex As FormatException
                        periodo = 0
                    End Try
                    ' Altera o timer para disparar usando o novo intervalo
                    If periodo > 0 Then
                        Console.WriteLine("{0} : Mudando o intervalo do Timer.", DateTime.Now.ToString("HH:mm:ss.ffff"))
                        threadTimer.Change(0, periodo)
                    End If
                Loop While periodo > 0
            End Using
            ' Aguarda...
            Console.WriteLine("O método principal foi completado. Pressione Enter.")
            Console.ReadLine()
        End Sub

        Private Shared Sub timerAcionado(ByVal estado As Object)
            Console.WriteLine("{0} : {1}", DateTime.Now.ToString("HH:mm:ss.ffff"), estado)
        End Sub

    End Class
End Namespace

Executando o programa iremos obter:

A classe Timer do namespace System.Threading é um timer mais leve e fácil de usar e que usa os métodos callback e é servido por uma thread do pool de threads.

Usando Timer em aplicações Windows Forms

Não é recomendado a sua utilização em aplicações Windows Forms, pois o callback não ocorre na thread da interface do usuário. Para estas aplicações é recomendado usar System.Windows.Forms.Timer.

No exemplo abaixo, apenas a título de ilustração, vemos como é simples usar a classe Timer do namespace System.Windows.Forms para criar um relógio digital.

Abra o Visual Basic 2008 Express Edition e crie um novo projeto do Aplicativo Windows Forms com o nome relogioDigital.

A seguir inclua o código abaixo no formulário:

Imports System
Imports System.Drawing
Imports System.Windows.Forms

Class DigitalClock Inherits Form
    
    Public Shared Sub Main()  
        Application.Run(New DigitalClock())
    End Sub

    Public Sub New()
        ResizeRedraw = True
        Dim relogio As New Timer()
        AddHandler relogio.Tick, AddressOf TimerOnTick
        relogio.Interval = 1000
        relogio.Start()
    End Sub

    Private Sub TimerOnTick(ByVal obj As Object, ByVal ea As EventArgs)
        Invalidate()
    End Sub

    Protected Overloads Overrides Sub OnPaint(ByVal pea As PaintEventArgs)
        Dim grafico As Graphics = pea.Graphics
        Dim dt As DateTime = DateTime.Now

        Dim strHora As String = (dt.ToString("d") & vbLf) + dt.ToString("T")

        Dim tamanhoFonte As SizeF = grafico.MeasureString(strHora, Font)
        Dim escalaFonte As Single = Math.Min(ClientSize.Width / tamanhoFonte.Width, ClientSize.Height / tamanhoFonte.Height)
        Dim fonte1 As New Font(Font.FontFamily, escalaFonte * Font.SizeInPoints)

        Dim strfmt As New StringFormat()
        strfmt.Alignment = InlineAssignHelper(strfmt.LineAlignment, StringAlignment.Center)

        grafico.DrawString(strHora, fonte1, New SolidBrush(ForeColor), ClientRectangle, strfmt)
    End Sub

    Private Shared Function InlineAssignHelper(Of T)(ByRef destino As T, ByVal valor As T) As T
        destino = valor
        Return valor
    End Function
End Class

Executando o projeto você terá o seu relógio digital:

Eu sei é apenas VB .NET, mas eu gosto...

Referências:


José Carlos Macoratti