1

So i've attempted to modify the code block posted by Sriram Sakthivel here:

C#: How to start a thread at a specific time

public class Program
{
    static Boolean checkIn = false;

    public static void checkInCycle()
    {
        checkIn = SynchronousSocketClient.StartClient();

        if(checkIn == false)
        {
            // Use TimeSpan constructor to specify:
            // ... Days, hours, minutes, seconds, milliseconds.
            TimeSpan span = new TimeSpan(00, 00, 30);

            //SetUpTimer(span);
            DateTime current = DateTime.Now;
            TimeSpan triggerTime  = current.TimeOfDay + span;

            SetUpTimer(triggerTime);
        }
        if(checkIn == true)
        {

            //Do some processing 
        }



    }

    public static void SetUpTimer(TimeSpan alertTime)
    {
        //DateTime current = DateTime.Now;
        //TimeSpan timeToGo = alertTime - current.TimeOfDay;

        TimeSpan timeToGo = alertTime;
        Console.WriteLine("Checking in at: " + timeToGo);

        if (timeToGo < TimeSpan.Zero)
        {
            return; //time already passed
        }
        System.Threading.Timer timer = new System.Threading.Timer(x =>
        {
            Console.WriteLine("Running CheckIn Cycle");
            checkInCycle();

        }, null, timeToGo, Timeout.InfiniteTimeSpan);

    }

    public static int Main(String[] args)
    {
        checkInCycle();

        Console.WriteLine("End of Program Reached");
        Console.ReadLine();

        return 0;

    }
}

However instead of specifying an exact time to run at, i've attempted to add 30 mins onto the current time in an attempt to make a client service that will remain alive for x amount of minutes before attempting to connect again. Right now, for simplicity/testing sake i have it set to run checkInCycle every 30 seconds if it cant connect to the server.

Upon first check SynchronousSocketClient.StartClient(); succesfully returns false if the server is down, and will enter the if(checkIn == false) loop - however, after setting up a timer it moves on to process the rest of the main loop, and waits at the end without triggering and rescheduling a timer.

Any ideas as to why this is happening? Also I understand i could just sleep the main thread for x amount of minutes before rechecking again, but the client could be asleep for up to several hours and because of this i have heard that timers are more efficient, is this the case?

Community
  • 1
  • 1
amartin94
  • 505
  • 3
  • 19
  • 35
  • 3
    Your timer is probably getting garbage collected because it is a local variable, make it a class level variable/field. – Ben Robinson Sep 02 '15 at 15:16
  • Your trigger time should just be the `span`, don't add the time of the day to it. – Ron Beyer Sep 02 '15 at 15:18
  • My apologies. You need to keep the Timer's reference to prevent it from being garbage collected. I've updated my answer in the original post to reflect that change. – Sriram Sakthivel Sep 02 '15 at 15:41

2 Answers2

4

This is happening because at the end of your execution of the main() method you are losing the reference to your System.Threading.Timer. The new System.Threading.Timer call does not block the main thread while it waits to run its code.

In the above reference (second note under Remarks), the documentation explains that a reference must be maintained (as with all managed code) for it not to be disposed of by the Garbage Collector.

As long as you are using a Timer, you must keep a reference to it. As with any managed object, a Timer is subject to garbage collection when there are no references to it. The fact that a Timer is still active does not prevent it from being collected.

The way you can keep this simple program is if you use a System.Threading.Monitor and perform a Wait() directly after the checkInCycle() function call in the main() function. Then Pulse() when you want to proceed.

You may want to read up on thread synchronization or concurrency further if you are curious about any more in-depth topics on performing asynchronous calls or operating in a mutli-threaded environment.

*edited for clarification.

int_541
  • 107
  • 3
Andrew
  • 381
  • 1
  • 7
  • Is the Wait() and Pulse() necessary though? I mean, right now i just added a reference to the Timer outside the scope of the class as suggested, and the code just sits on ReadLine() and loops the timers. Is that bad practice for a service? Is there a more gracefull way to do this instead of having the program sit on ReadLine() in order to stop it from closing – amartin94 Sep 02 '15 at 15:59
  • A bad practice, no. However, the console application could close if someone accidentally hit the "enter" button. The Monitor class would require a specific intention as you would have to program the Pulse() method into some action required by the user. This sounds more like a service and as such I (personally) would expect to have clearly defined start and stop actions that I could perform. [.NET Windows Service Application](https://msdn.microsoft.com/en-us/library/zt39148a(v=vs.110).aspx) – Andrew Sep 02 '15 at 16:13
1

Additionally to the Andrew's answer, the way you modified SetupTimer, you should not add the current time and just pass directly the retry time. Otherwise you have to wait a couple hours :-)

Community
  • 1
  • 1
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343