- Timer in Visual Basic .Net -
 
COSA SERVE PER QUESTO TUTORIAL
Download | Chiedi sul FORUM | Glossario cognizioni basiche di un VB .Net
Qual è il timer migliore in .Net?

CONFRONTO TRA I TRE TIMER DISPONIBILI
Le classi timer di System.Threading, System.Windows.Forms e System.Timers.

È piuttosto frequente la necessità di eseguire delle azioni a intervalli di tempo ben specificati, e ovviamente quesot in .Net è possibile, tuttavia si potrebbe rimanere inizialmente disorientati dalle possibilità che il framework offre. Esistono infatti ben tre tipi di timer differenti: System.Windows.Forms.Timer, System.Threading.Timer, System.Timers.Timer. Vediamone le caratteristiche:

  • System.Threading.Timer: questo timer è molto leggero ed efficiente, permette di impostare una funzione da chiamare allo scadere di ogni intervallo e di passargli un parametro; tuttavia è eseguito su un thread indipendente da quello in cui è stato creato, in particolare su uno di quelli disponibile nel ThreadPool di sistema; ciò significa che l'utilizzo di questo timer porta con sé tutte le complicazioni che la programmazione parallela comporta;
  • System.Windows.Forms.Timer: timer particolarmente adatto per eseguire processi temporizzati che riguardano l'interfaccia grafica (è significativo che si trovi infatti in System.Windows.Forms) che si utilizza come un qualsiasi controllo (inseribile dalla Toolbox); allo scadere dell'intervallo di tempo impostato viene invocato un evento Tick, con precisione per valori superiori a 55 millisecondi; utilizza un solo thread, inserendosi nella coda dei messaggi dell'interfaccia grafica, ciò significa che se l'interfaccia grafica smette di rispondere anche l'evento temporizzato non verrà eseguito;
  • System.Timers.Timer: come il timer di System.Threading si basa su un thread pool e per questo è maggiormente accurato di System.Windows.Forms.Timer, ma al contrario del primo è thread-safe, ovvero permette di scegliere tramite la proprietà SynchronizingObject in quale thread eseguire l'evento Elapsed (ovvero quello che indica che è scaduto l'intervallo); ad esempio se si imposta la proprietà SynchronizingObject su un form o un controllo, l'evento verrà sollevato nel thread che lo ha creato evitando i tipici problemi del parallelismo;

Brevemente, un ThreadPool è un "bacino" di thread che l'applicazione ha a disposizione e a cui può attingere; questi thread sono chiamati worker-thread e svolgono vari compiti di sistema o imposti dall'utente come in questo caso o ogni volta che si chiamano i metodi asincroni che iniziano per Begin o End (es. BeginRead, EndConnect e così via).
Ma scendiamo ora nel dettaglio e vediamo come si possono utilizzare. Vogliamo realizzare una semplicissima applicazione che scriva ogni 50 millisecondi un numero casuale in tre label tramite i tre possibili tipi di timer visti. Creiamo un nuovo progetto Windows Forms e prepariamo tutti i nostri timer:


Public Class frmMain
    Private tmrThread As Threading.Timer
    Private tmrTimer As System.Timers.Timer
    Private tmrWindowsForms As System.Windows.Forms.Timer

    Private rnd As New Random()
    Const intInterval As Integer = 50

    Private Sub frmMain_Load(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles MyBase.Load

        Control.CheckForIllegalCrossThreadCalls = False

        tmrThread = New System.Threading.Timer(AddressOf NewRandomNumber, _
            lblSystemThreading, 0, intInterval)

        tmrTimer = New System.Timers.Timer(intInterval)
        AddHandler tmrTimer.Elapsed, AddressOf tmrTimer_Elapsed
        tmrTimer.SynchronizingObject = Me
        tmrTimer.Start()

        tmrWindowsForms = New System.Windows.Forms.Timer()
        tmrWindowsForms.Interval = intInterval
        AddHandler tmrWindowsForms.Tick, AddressOf tmrWindowsForms_Tick
        tmrWindowsForms.Start()
    End Sub


    Private Sub frmMain_FormClosed(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed

        Dim ewh As New System.Threading.EventWaitHandle(True, _
            Threading.EventResetMode.AutoReset)
        tmrThread.Dispose(ewh)
        ewh.WaitOne()

        tmrWindowsForms.Stop()
        tmrTimer.Stop()
    End Sub
    
    [...]
    
End Class
        

Prima di tutto abbiamo dichiarato i tre tipi di timer, un generatore di numeri casuali (che verrà usato nella funzione di callback) e una costante per indicare i millisecondi di intervallo per tutti i timer.
In frmMain_Load, per semplicità, impostiamo la proprietà Control.CheckForIllegalCrossThreadCalls su False, in modo da non ricevere errori quando effettuiamo chiamate su controlli del form da thread diversi da quello da cui sono state create. Ciò succede solamente con il timer di System.Threading, che come abbiamo detto non è thread-safe. In realtà bisognerebbe utilizzare le funzioni BeginInvoke e EndInvoke di un controllo per effettuare questo tipo di modifiche, ma per brevità tralasciamo questo dettaglio (nelle applicazioni pratiche non trascurabile).
In seguito creiamo un'istanza di System.Threading.Thread al quale passiamo il riferimento ad una funzione, un argomento che essa riceverà, il tempo dopo il quale avviare il timer e l'intervallo con il quale invocare il delegato.
Per System.Timers.Timer è tutto abbastanza intuitivo, se non l'uso della proprietà SynchronizingObject che impostiamo sul form stesso in modo che l'evento Elapsed, in questo caso la funzione tmrTimer_Elapsed, sia invocata nello stesso thread del controllo evitando così i problemi di dover effettuare chiamate cross-thread (questo timer infatti non darebbe problemi anche se CheckForIllegalCrossThreadCalls fosse impostato su True).
Per System.Windows.Forms.Timer, non vi è nulla di nuovo.
Nella Sub frmMain_FormClosed ci preoccupiamo di interrompere tutti i nostri timer, in particolare per quello di Threading dobbiamo assicurarci che sia terminata l'ultima esecuzione prima di chiudere il form altrimenti, potrebbe tentare di accedere ad un controllo non più disponibile.

 

<< INDIETRO by VeNoM00