How to have a loop in a Windows service without using the Timer
Asked Answered
C

5

26

I want to call a Business layer method from a Windows service (done using C# and .NET) after every 10 seconds. However, i dont want to use the Timer_Elapsed event since it starts up another thread/process if the first thread/process is still running. I just need a single threaded approach, since multiple calls to the same Business method creates unwanted complications.

So i added a do--while loop in the on_start. I know this is not the correct way since it spawns this process which becomes an orphan if the service is shut down.

How can i approach this problem ?

Regards, Chak

Compulsion answered 9/1, 2010 at 8:26 Comment(2)
If you want to use only a single thread then the loop sounds like the correct approach - I assume that you have included a call to Thread.Sleep to ensure that the method is not called in a tight loop. Can you provide details of how you are calling the business layer method? If you are calling it in process then this should give you the desired result.Husking
Ok thanks. I am calling it in process. However, i find that even if i shutdown the service it is running in Task manager. I am experimenting by a flag which is switched when the business method is called and do not call it again unless the flag has been reset. I hope it works. Otherwise i shall try what you have suggested. The problem is that the Business method reads and writes to a Serial port till it gets data or times out.Compulsion
P
56

There's another way to get timed execution, the WaitHandle.WaitOne() method provides a timeout argument. That works very nicely in a service as it lets you implement the need to stop the service and periodic execution in a single method call. The template looks like this:

    Thread Worker;
    AutoResetEvent StopRequest = new AutoResetEvent(false);

    protected override void OnStart(string[] args) {
        // Start the worker thread
        Worker = new Thread(DoWork);
        Worker.Start();
    }
    protected override void OnStop() {
        // Signal worker to stop and wait until it does
        StopRequest.Set();
        Worker.Join();
    }
    private void DoWork(object arg) {
        // Worker thread loop
        for (;;) {
            // Run this code once every 10 seconds or stop right away if the service 
            // is stopped
            if (StopRequest.WaitOne(10000)) return;
            // Do work...
            //...
        }
    }
Pottage answered 9/1, 2010 at 13:1 Comment(3)
I got a service running at my company based on the above code. It works fine.Kinghood
what will happen if "Do Work" takes more than 10 seconds this case?Lebbie
@vampire203: The main loop will wait 10 seconds and then do the work so the total execution time of the loop is the time it takes to do the work plus 10 seconds. This was more or less the behavior that was requested in the question.Bobettebobina
B
4

Use a timer, but as soon as you enter the Timer handler method, disable the timer so that no more events are raised. Just before exiting the handler, re-enable the timer.

Backrest answered 9/1, 2010 at 8:31 Comment(3)
Thanks. But if i do this, it will call the Business method once and then never run again ? The Business method in turn reads or writes to a serial port and so it should return after one read / write / timeout and then repeat it after some time.Compulsion
it @Chakravarthy: provided you re-enable timer, before leaving the handler, it will continue to run.Backrest
Since the handler method is invoked on a separate thread, it could theoretically be called multiple times before the timer is disabled.Administrative
A
4

Check out this discussion, and in particular the answer by jsw. It suggests a synchronization mechanism to prevent multiple simultaneous calls to the business logic. Just disabling the timer in the Elapsed handler method isn't guaranteed to prevent parallel calls since the handler is invoked on a separate thread. Use a lock as jsw suggests, and stop the timer within the synchronized code block.

Alternatively, you could use a Timer and set the AutoReset property to false. That way, the Elapsed event is raised only once and you can reset the timer manually towards the end of the handler method.

Administrative answered 9/1, 2010 at 9:21 Comment(0)
S
-3
while(true)
    {
     ..do something
     Thread.sleep( some time or day);
    }
Sufferable answered 2/7, 2012 at 8:12 Comment(0)
E
-3
    Thread thread;
    private void DoWork(object arg)
    {
        while (true)
        {
            // Run this code once every 20 seconds or stop if the service is stopped
            try
            {
                Thread.Sleep(20000);
                //Do work....
            }
            catch(ThreadInterruptedException)
            {
                return;
            }

        }
    }

    protected override void OnStart(string[] args)
    {
        // Start the thread
        thread = new Thread(DoWork);
        mWorker.Start();
    }

    protected override void OnStop()
    {
        // interrupt thread and wait until it does
        thread.Interrupt();
        thread.Join();
    }
Echikson answered 31/10, 2013 at 9:48 Comment(1)
Thread.Sleep is not the ideal solution, why ? see this https://mcmap.net/q/74694/-why-is-thread-sleep-so-harmfulHilarius

© 2022 - 2024 — McMap. All rights reserved.