views:

547

answers:

6

I have a Windows Service which is basically accessing a mailbox and reading the emails. I am trying to figure out the best way to check the mailbox every x seconds.

One method is to just call Thread.Sleep, then call the Start Method again, e.g.

protected override void OnStart(string[] args)
{
    // get config settings
    CheckMailbox();
}

public void CheckMailbox()
{
    int x = 5000;

    // do stuff

    Thread.Sleep(x);
    CheckMailbox();
}

Not sure if this is the best way to go about it. To then further explore this, I understand that you can call a Windows Service by exposing the Service via WCF. In this case, if a web application called the Process to start, there would be conflicting Threads am I correct? How would I then deal with that? Would I have to create a new thread each time and put it in a queue?

+5  A: 

Sleeping is usually a bad idea. Apart from anything else, it means that you won't respond to the Stop event quickly.

You should use a Timer. There are two which are appropriate for services:

In each case you basically say what you want to happen each time the timer "ticks" and when you want it to fire.

My threading tutorial has a section explaining some of the differences between available timers.

I'm not sure about your WCF question, but I don't think your web service would be explicitly starting the service - it would just be contacting it via WCF. Yes, you'd potentially need to be careful about threads. Exactly what you'll need to be careful of will depend on what the WCF service is exposing.

Jon Skeet
+1 - ah, good point! While sleeping, the service won't react to stop events, that's true.
marc_s
With the Threading timer, say I check the mailbox every 10 secs, and it takes longer then 10secs to open and process, this would have multiple threads running at once correct?
mickyjtwin
+1 Timers are useful! @mickyjtwin: If you use the System.Timers.Timer it has Start and Stop methods, so you can call Stop, process your mails, and then call Start. That way you will wait x seconds after finishing processing your mails until checking next time. However, I don't think you would get parallell threads in the case you suggest; the Tick should be invoked on the thread where the timer was created, not on a new thread.
Fredrik Mörk
I used an example whereby the Threading.Timer called every 2 seconds, and the callback slept for 10 secs. The callback is created on a new thread every 2 secs. If the process takes longer than 2 secs, then a new thread will be doing a parallel process. How do you use the non Threading Timer to call every x seconds?
mickyjtwin
What do you want to happen if the process takes longer than the interval? It's easy to make it wait for the interval time at the end of each iteration - just use a Timers.Timer, set AutoReset to false, and call Start() at the end of each iteration.
Jon Skeet
A: 

If you want to have it run as a service (in the background, without anyone logged in), yes, this is one way to do it.

Another would be to have a Timer that calls its "Tick" event handler every 5 seconds or so.

Or if your checks aren't quite as frequent, you could also create a command-line console app and just schedule it as a "scheduled task" in Windows.

Marc

marc_s
A: 

Your code has the problem of adding to the call stack for every call, since you call the method from within itself. At some point you will get a stack overflow exception. One wait is to call another method on a "waiting thread" that will signal back after a certain time:

private void CheckMailBox()
{
    _waitHandle = new AutoResetEvent(true);
    while (_waitHandle.WaitOne())
    {
        // do the mail checking
        ((IDisposable)_waitHandle).Dispose();
        _waitHandle = new AutoResetEvent(false);
        ThreadPool.QueueUserWorkItem(WaitSomeTime, 2000);
    }

}

private void WaitSomeTime(object state)
{

    Thread.Sleep((int)state);
    _waitHandle.Set();
}
Fredrik Mörk
+1  A: 

Basic System.Threading.Timer code:

// Class level variable in the Windows Service:
private System.Threading.Timer timer;

// This is in the constructor for a Windows Service.
// interval would be the time in milliseconds in between ticks.
// 0 is how long until it should start.
timer = new Timer(new TimerCallback(TimerElapsed), null, 0, interval);

// The TimerCallback in the Windows Service:
private void TimerElapsed(object o)
{
    // Do email stuff here.
}
Gromer
A: 

A possible enhancement beyond using a timer would be to use a scheduling component in your service. Then you could setup your service to be the host for the code that would access your mailbox.

Quartz.NET is an open source scheduling component that can do this. It allows you to setup the scheduling exactly as you want and it is quite easy to use.

Rune Grimstad
A: 

Ok, while your timing question is pretty much handled (and i would agree with most of the above), I can offer some info concerning WCF. A WCF service is simply hosted within an application domain. This domain could be a windows form, console application, ASP.net and in this case, a Windows Service.

So in a sense, you're not exposing the windows service via WCF, you're more hosting the WCF service within a Windows Service application domain. I would advise testing your service (timer objects included) in a windows form before making the transition to windows service. Easier to debug and test.

A hosted WCF service is in fact running in its own thread:

    serviceType_MyEmailer = typeof(MyEmailerServiceType);
    ServiceHost host_MyEmailer;


    host_MyEmailer = new ServiceHost(serviceType_MyEmailer);

Look for a "DerivativesCalculatorService" example online. Shows you how to host a WCF service in code.

The timing trick here is in within your service itself. You could create your service class to be a singleton class that gets initialized immediately, and upon its initialization it could start its Timer object (as seen with the other answers).

In this way, your WCF service would have a persistent instance of your class with its Timer object silently ticking off. Have all of that hosted in a Windows Service and you're done.

Hope this helps.

MoSlo