Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations John Tel on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

System.Threading.Timer Basic Question 2

Status
Not open for further replies.

Genimuse

Programmer
May 15, 2003
1,797
US
I'm sure I'm missing something very basic here, am pretty much a newb in many ways, but two days of Googling and adjusting things haven't brought me an answer, so here's my question.

Currently I'm using a Timer control in an app (this is on the Compact Framework 2.0, but I don't think it matters). It works fine, fires every 10th of a second, checks the status of a port listener, does some utility work now and then, etc. The code that fires is in the Tick event.

I'm changing this to a console application, though, so don't have the Timer control there. Folks here helpfully suggested using System.Threading.Timer, which is indeed available to me (System.Timers is not, btw).

I think I get how these guys are supposed to work, but I'm having trouble. Here's a super basic application:
Code:
Module Core

  Private timerCount As Integer = 0

  Sub Main()
    Dim myTimer As New System.Threading.Timer(AddressOf timerCallback, Nothing, 1000, 1000)
  End Sub

  Sub timerCallback(ByVal state As Object)
    timerCount += 1
  End Sub

End Module
It seems to me, in my limited understanding, that if I start this application it will start the timer and every second it will call timerCallback, which will increment the counter.

What actually happens is the application starts up and then quits, all done, no errors. Like it ran and now it's finished, y'know?

What will make it... "run," if you will, just keep timing and running until I quit it? Does this relate to it being a console application?

Thanks much.
 
You need something to keep the console alive, food, beer or a system.console.in.readline().

Code:
Module Module1

    Private timerCount As Integer = 0

    Sub Main()
        Dim myTimer As New System.Threading.Timer(AddressOf timerCallback, Nothing, 1000, 1000)
        System.Console.In.ReadLine()
    End Sub

    Sub timerCallback(ByVal state As Object)
        timerCount += 1
        System.Console.Out.Write(timerCount)
    End Sub

End Module

Christiaan Baes
Belgium

"Time for a new sig." - Me
 
Often, I'll use a ManualResetEvent to keep the message pump going. When your user chooses to exit the app, or you find out the system is shutting down (etc.etc), you Set() it, and it then falls out from your Main method.

It has other advantages -- when you're doing a async read (like on a socket) you can add it to the array of WaitHandles you're waiting on, and do a WaitHandle.WaitAny() on it. When this method returns it tells you which handle got set, and if it was your shutdown handle, you know to start closing & disposing stuff.

One other advantage is that it allows the CPU to go into power-save mode (unlike polling), so your portable device's battery lasts longer.

Chip H.


____________________________________________________________________
If you want to get the best response to a question, please read FAQ222-2244 first
 
Chrissie, I tried that but it had no effect.

Chip, that sounds perfect. Would you explain how to do it, or could you point me to something that does? I looked into ManualResetEvent (and AutoResetEvent), but couldn't figure out how to implement them. Unfortunately my knowledge to OOP and threading is extremely limited, though I'm working hard to learn.
 
Here's a sample.

Code:
public class myclass
{
   private ManualResetEvent _stop;

   public void Connect()
   {
      _stop = new ManualResetEvent(false);
      _workerThread = new Thread( new ThreadStart(EngineWorkerThread));
      _workerThread.Start();
   }

   public void Disconnect()
   {
      _stop.Set();
   }

   private void EngineWorkerThread()
   {
       // Do stuff that takes a long time

       WaitHandle[] wh = {_stop};
       WaitHandle.WaitAny(wh);
   }
}

Outside code (like the OnStart event of a Service) calls the Connect event, which starts the other thread and returns immediately. The worker thread does stuff, then waits for the main thread to signal it's time to stop via some other thread calling the Disconnect method.

Note that while you could do this same thing with a boolean value, the .net bool type is actually not thread-safe because it's stored as a double-word in memory. Use the provided synchronization classes instead.

Chip H.

____________________________________________________________________
If you want to get the best response to a question, please read FAQ222-2244 first
 
Whoops, sorry about that -- posting in C# again. Lemme see if I can turn it into soemthing resembling VB.NET code:
Code:
public class myclass

   Dim stopEvt as ManualResetEvent

   public sub Connect()
      stopEvt = new ManualResetEvent(false)
      Dim workerThread As Thread = new Thread( new ThreadStart(EngineWorkerThread))
      workerThread.Start()
   End Sub

   public sub Disconnect()
      stopEvt.Set()
   End Sub

   private sub EngineWorkerThread()
       // Do stuff that takes a long time

       Dim wh as WaitHandle(1)
       wh(1) = stopEvt
       WaitHandle.WaitAny(wh)
   End Sub
End Class


____________________________________________________________________
If you want to get the best response to a question, please read FAQ222-2244 first
 
pretty close chiph

just remeber to swap the // with '

Christiaan Baes
Belgium

"Time for a new sig." - Me
 
Thanks to you both.

A couple of questions, as I'm uncertain how to use the code.

In the line:
Dim workerThread As Thread = new Thread( new ThreadStart(EngineWorkerThread))
what does EngineWorkerThread refer to? Some other thread that I'd be using? The app isn't threaded right now.

In the line:
WaitHandle.WaitAny(wh)
What is the WaitAny method? Threading.WaitHandle doesn't seem to have any methods or properties.
 
corrected version

Code:
    Dim stopEvt As ManualResetEvent

    Public Sub Connect()
        stopEvt = New ManualResetEvent(False)
        Dim workerThread As Thread = New Thread(New ThreadStart(AddressOf EngineWorkerThread))
        workerThread.Start()
    End Sub

    Public Sub Disconnect()
        stopEvt.Set()
    End Sub

    Private Sub EngineWorkerThread()
        'Do stuff that takes a long time

        Dim wh(1) As WaitHandle
        wh(1) = stopEvt
        WaitHandle.WaitAny(wh)
    End Sub

Christiaan Baes
Belgium

"Time for a new sig." - Me
 
I'm working in the Compact Framework (2.0) and have discovered that Waithandle only has WaitOne() (along with GetType, ReferenceEquals, and ToString). Is there a way to make that work without WaitAny()?

Here's something I kinda cobbled together from other examples I've seen out there that seems to work, though I'm not sure if I'm burning processor cycles or something, as I don't fully understand what's going on:
Code:
Public Class appTimer
  Shared Sub Main()
    Dim autoEvent As New Threading.AutoResetEvent(False)
    Dim statusChecker As New StatusChecker(1000)
    [COLOR=green]' Create the delegate that invokes methods for the timer.[/color]
    Dim timerDelegate As Threading.TimerCallback = AddressOf statusChecker.CheckStatus
    [COLOR=green]' Create a timer that signals the delegate to invoke 
    ' CheckStatus after one second, and every 1/2 second thereafter[/color]
    Dim stateTimer As Threading.Timer = New Threading.Timer(timerDelegate, autoEvent, 1000, 500)
    While True
      autoEvent.WaitOne(500, False)
    End While
  End Sub
End Class

Public Class StatusChecker
  Dim invokeCount, maxCount As Integer

  Sub New(ByVal count As Integer)
    invokeCount = 0
  End Sub

  [COLOR=green]' This method is called by the timer delegate[/color]
  Sub CheckStatus(ByVal stateInfo As Object)
    Dim autoEvent As Threading.AutoResetEvent = DirectCast(stateInfo, Threading.AutoResetEvent)
    invokeCount += 1

    [COLOR=blue]'DO MY STUFF HERE[/color]

  End Sub
End Class
This seems to fire every 1/2 second, but I'm worried that the While True loop is doing something besides just reseting the check to every 1/2 second (where it already was).

Sorry for such a dense post. If there's a way to do the method you guys are suggesting without WaitAny, I'm all for that, of course.
 
WaitOne will work as well. The differences are:

WaitOne Waits for one synchronization object (something that inherits from WaitHandle) to be signaled.
WaitAny Waits for any one synchronization object to be signaled in an array of synchronization objects.
WaitAll Waits for all synchronization objects in an array to be signaled before continuing.

I tend to use WaitAny because I write a lot of async communications code -- I can wait on a shutdown event, a data-arrived event, and optionally a timeout event. When it returns, I check to see which one happened and make a decision about what to do.

Since the compact framework only supports WaitOne, then that's the one you have to use!

I don't think you need the while..true loop. Your timer ought to be firing every 1/2 second without it. Your timer constructor probably ought to be:
Code:
    Dim stateTimer As Threading.Timer = New Threading.Timer(timerDelegate, autoEvent, 0, 500)
Which tells the timer to call this delegate, passing autoEvent as a parameter, start immediately, and repeat the call every 500ms. You then make your call to WaitOne, where execution stops and waits until signaled.

When something in your app (either in the timer code or elsewhere) sets the autoEvent object, your code in the original thread will then continue past the WaitOne statement.

Chip H.

BTW: thanks for correcting my code, chrissie1!


____________________________________________________________________
If you want to get the best response to a question, please read FAQ222-2244 first
 
My pleasure, actually it was intellisense that did most of the work.

Christiaan Baes
Belgium

"Time for a new sig." - Me
 
After ages and ages of wrestling, I ended up going with the code I posted. WaitOne required some kind of parameter, but was very unhappy with the WaitHandle and anything else I thought made sense.

Anyway, the code is functioning: every 1/2 second it calls statusChecker.CheckStatus (it does require the While loop, otherwise it just exits after 1/2 second).

I now need to stop the timer while I do something and then restart it. I thought that autoEvent.Set() would stop it, but that doesn't appear to be the case. How do I stop it?
 
If you need to stop the timer, set it to Nothing and recreate it. Don't forget that a timer object needs to be disposed.

If you only need to change it's interval, use the Change() method.

Chip H.


____________________________________________________________________
If you want to get the best response to a question, please read FAQ222-2244 first
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top