How to stop an ongoing timer linked to a timed action - c#

I use System.Timers.Timer to start a timer. There is an event that
is raised occasionally, every time this event raises I increment a counter.
If the counter > 3, I execute a method to perform an action and I need to repeatedly do this.
I reset the counter every N seconds to start counting the events again from 1.
When the counter resets to 0, I need to stop the ongoing action that gets triggered by the timer, but I can't get it to stop and this method keeps on executing even after the counter has reset to 0.
My program has this structure:
class Program
{
static void Main(string[] args)
{
var counter = 0;
//Create timer
var delayTimer = new System.Timers.Timer
{
// Interval = 900000, // every 15 minutes
Interval = 45000, // debug
AutoReset = true,
};
// Start the timer
delayTimer.Start();
delayTimer.Elapsed += ((o, e) =>
{
// when timer elapses reset the counter
counter = 0;
// stop the timed action function which was executing every N seconds for counter > 3
TimedTask(1, false, "stop"); // stop the timer
});
// an event raises the counter
while (event)
{
counter++; // keeps track of events raised
if (counter > 3)
{
// take a specific action
TimedTask(30000, true); // start the timer to perform an action every 30 seconds
}
}
// to keep the application going
System.Windows.Forms.Application.Run();
}
}
// method to start a timer for repeating a task every N seconds, or stop the counter for this ongoing timed task
public static void TimedTask(int interval, bool autoreset, string stop = null)
{
//Create timer
var delayTimer = new System.Timers.Timer
{
Interval = interval, // every N seconds
AutoReset = autoreset, // true: repeat, false: don't repeat
};
if (stop != null)
{
delayTimer.Elapsed += ((o, e) =>
{
// if 'stop' passed as string, stop executing the task every N seconds
delayTimer.Stop();
});
}
else
{
// Start the timer
delayTimer.Start();
// Create the event handler
delayTimer.Elapsed += ((o, e) =>
{
// when timer elapses call the method below
RepeatedAction();
});
}
}
// repeated action method
public static void RepeatedAction()
{
// perform an action, repeat it every N seconds
}
}
I have tried sending an additional string parameter "stop" to un-register the timer but this does not work.
I am creating two different timers: one for resetting the counter and one for repeating the timed task, could I minimise this by creating a timer method or something? Thanks in advance.

Related

Windows Service recursive & infinite call

I have a Windows Service which connects to an external device (micro controller) to get data and save it to database. The external device does not support callback feature, that's why the service should ask for the data frequently.
What I did so far: I created a service with a timer which ticks frequently, based on configuration.
Here, I have two problems:
Sometimes the data very big, and before collecting and saving that data, the timer is restarting based on configuration. In this case, I'm losing the data.
If I configure the timer for longer period, I'm losing monitoring which data should be monitored immediately after collect.
I've read other related questions like doing a recursive call in the background service, etc. But I don't have exit condition for recursive call. Application should continue working even after some exception occurs.
Here is my code:
public partial class DataManagerService : ServiceBase
{
public DataManagerService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
timer = new System.Timers.Timer(1000);
timer.Elapsed += new System.Timers.ElapsedEventHandler(OnElapsedTime);
timer.Enabled = true;
}
private void OnElapsedTime(object source, System.Timers.ElapsedEventArgs e)
{
GetRecords();
}
protected override void OnStop()
{
// clean up
}
}
PS: I'm using .NET Framework 4.6.1 and Visual Studio 2017 Community edition
The System.Timers.Timer.Stop documentation contains a (non-trivial) example of how to accomplish this using two Timer instances. At each interval, a separate control Thread leverages:
a static currentTimer field to select the correct Timer instance
the System.Threading.Interlocked.CompareExchange static method to prevent overlapping Elapsed events.
Because I see a little ambiguity in your requirement, let me call out Microsoft's note about the use case for this approach versus System.Threading.Monitor:
If it were necessary to execute every event, the Monitor class would be a better way to synchronize the events.
From Microsoft docs System.Timers.Timer.Stop examples
The following code example shows one way to prevent the thread that calls the Stop method from continuing until a currently executing Elapsed event ends, and also to prevent two Elapsed events from executing the event handler at the same time (often referred to as reentrancy).
The example executes 100 test runs. Each time the test is run, the timer is started with an interval of 150 milliseconds. The event handler uses the Thread.Sleep method to simulate a task that randomly varies in length from 50 to 200 milliseconds. The test method also starts a control thread that waits for a second and then stops the timer. If an event is being handled when the control thread stops the timer, the control thread must wait until the event is finished before proceeding.
The Interlocked.CompareExchange(Int32, Int32, Int32) method overload is used to avoid reentrancy and to prevent the control thread from continuing until an executing event ends. The event handler uses the CompareExchange(Int32, Int32, Int32) method to set a control variable to 1, but only if the value is currently zero. This is an atomic operation. If the return value is zero, the control variable has been set to 1 and the event handler proceeds. If the return value is non-zero, the event is simply discarded to avoid reentrancy. (If it were necessary to execute every event, the Monitor class would be a better way to synchronize the events.) When the event handler ends, it sets the control variable back to zero. The example records the total number of events that executed, that were discarded because of reentrancy, and that occurred after the Stop method was called.
The control thread uses the CompareExchange(Int32, Int32, Int32) method to set the control variable to -1 (minus one), but only if the value is currently zero. If the atomic operation returns non-zero, an event is currently executing. The control thread waits and tries again. The example records the number of times the control thread had to wait for an event to finish.
using System;
using System.Timers;
using System.Threading;
public class Test
{
// Change these values to control the behavior of the program.
private static int testRuns = 100;
// Times are given in milliseconds:
private static int testRunsFor = 500;
private static int timerIntervalBase = 100;
private static int timerIntervalDelta = 20;
// Timers.
private static System.Timers.Timer Timer1 = new System.Timers.Timer();
private static System.Timers.Timer Timer2 = new System.Timers.Timer();
private static System.Timers.Timer currentTimer = null;
private static Random rand = new Random();
// This is the synchronization point that prevents events
// from running concurrently, and prevents the main thread
// from executing code after the Stop method until any
// event handlers are done executing.
private static int syncPoint = 0;
// Count the number of times the event handler is called,
// is executed, is skipped, or is called after Stop.
private static int numEvents = 0;
private static int numExecuted = 0;
private static int numSkipped = 0;
private static int numLate = 0;
// Count the number of times the thread that calls Stop
// has to wait for an Elapsed event to finish.
private static int numWaits = 0;
[MTAThread]
public static void Main()
{
Timer1.Elapsed += new ElapsedEventHandler(Timer1_ElapsedEventHandler);
Timer2.Elapsed += new ElapsedEventHandler(Timer2_ElapsedEventHandler);
Console.WriteLine();
for(int i = 1; i <= testRuns; i++)
{
TestRun();
Console.Write("\rTest {0}/{1} ", i, testRuns);
}
Console.WriteLine("{0} test runs completed.", testRuns);
Console.WriteLine("{0} events were raised.", numEvents);
Console.WriteLine("{0} events executed.", numExecuted);
Console.WriteLine("{0} events were skipped for concurrency.", numSkipped);
Console.WriteLine("{0} events were skipped because they were late.", numLate);
Console.WriteLine("Control thread waited {0} times for an event to complete.", numWaits);
}
public static void TestRun()
{
// Set syncPoint to zero before starting the test
// run.
syncPoint = 0;
// Test runs alternate between Timer1 and Timer2, to avoid
// race conditions between tests, or with very late events.
if (currentTimer == Timer1)
currentTimer = Timer2;
else
currentTimer = Timer1;
currentTimer.Interval = timerIntervalBase
- timerIntervalDelta + rand.Next(timerIntervalDelta * 2);
currentTimer.Enabled = true;
// Start the control thread that shuts off the timer.
Thread t = new Thread(ControlThreadProc);
t.Start();
// Wait until the control thread is done before proceeding.
// This keeps the test runs from overlapping.
t.Join();
}
private static void ControlThreadProc()
{
// Allow the timer to run for a period of time, and then
// stop it.
Thread.Sleep(testRunsFor);
currentTimer.Stop();
// The 'counted' flag ensures that if this thread has
// to wait for an event to finish, the wait only gets
// counted once.
bool counted = false;
// Ensure that if an event is currently executing,
// no further processing is done on this thread until
// the event handler is finished. This is accomplished
// by using CompareExchange to place -1 in syncPoint,
// but only if syncPoint is currently zero (specified
// by the third parameter of CompareExchange).
// CompareExchange returns the original value that was
// in syncPoint. If it was not zero, then there's an
// event handler running, and it is necessary to try
// again.
while (Interlocked.CompareExchange(ref syncPoint, -1, 0) != 0)
{
// Give up the rest of this thread's current time
// slice. This is a naive algorithm for yielding.
Thread.Sleep(1);
// Tally a wait, but don't count multiple calls to
// Thread.Sleep.
if (!counted)
{
numWaits += 1;
counted = true;
}
}
// Any processing done after this point does not conflict
// with timer events. This is the purpose of the call to
// CompareExchange. If the processing done here would not
// cause a problem when run concurrently with timer events,
// then there is no need for the extra synchronization.
}
// Event-handling methods for the Elapsed events of the two
// timers.
//
private static void Timer1_ElapsedEventHandler(object sender,
ElapsedEventArgs e)
{
HandleElapsed(sender, e);
}
private static void Timer2_ElapsedEventHandler(object sender,
ElapsedEventArgs e)
{
HandleElapsed(sender, e);
}
private static void HandleElapsed(object sender, ElapsedEventArgs e)
{
numEvents += 1;
// This example assumes that overlapping events can be
// discarded. That is, if an Elapsed event is raised before
// the previous event is finished processing, the second
// event is ignored.
//
// CompareExchange is used to take control of syncPoint,
// and to determine whether the attempt was successful.
// CompareExchange attempts to put 1 into syncPoint, but
// only if the current value of syncPoint is zero
// (specified by the third parameter). If another thread
// has set syncPoint to 1, or if the control thread has
// set syncPoint to -1, the current event is skipped.
// (Normally it would not be necessary to use a local
// variable for the return value. A local variable is
// used here to determine the reason the event was
// skipped.)
//
int sync = Interlocked.CompareExchange(ref syncPoint, 1, 0);
if (sync == 0)
{
// No other event was executing.
// The event handler simulates an amount of work
// lasting between 50 and 200 milliseconds, so that
// some events will overlap.
int delay = timerIntervalBase
- timerIntervalDelta / 2 + rand.Next(timerIntervalDelta);
Thread.Sleep(delay);
numExecuted += 1;
// Release control of syncPoint.
syncPoint = 0;
}
else
{
if (sync == 1) { numSkipped += 1; } else { numLate += 1; }
}
}
}
/* On a dual-processor computer, this code example produces
results similar to the following:
Test 100/100 100 test runs completed.
436 events were raised.
352 events executed.
84 events were skipped for concurrency.
0 events were skipped because they were late.
Control thread waited 77 times for an event to complete.
*/

Is there a way when application starts immediately method ExecuteEvery5Min gets execute and then through timer every 5 minute?

I'm using a Timer which invoke some piece of code ExecuteEvery5Min every 5 minute.
Now I start console application and I have to wait for 5 minutes and then code ExecuteEvery5Min is execute and then after every 5 minutes....
Is there a way when application starts and immediately code ExecuteEvery5Min execute and then through timer every 5 minute?
using (UtilityClass utilityClass = new UtilityClass()) // To dispose after the use
{
while (true) { }
}
public class UtilityClass : IDisposable
{
private readonly System.Timers.Timer _Timer;
public UtilityClass()
{
_Timer = new System.Timers.Timer(TimeSpan.FromMinutes(5).TotalMilliseconds)
{
Enabled = true
};
_Timer.Elapsed += (sender, eventArgs) =>
{
ExecuteEvery5Min();
};
}
private void ExecuteEvery5Min()
{
Console.WriteLine($"Every 5 minute at {DateTime.Now}");
}
public void Dispose()
{
_Timer.Dispose();
}
}
Why not simply call the code in the constructor (to have it immediately) on top of in the timer?
_Timer = new System.Timers.Timer(TimeSpan.FromMinutes(5).TotalMilliseconds)
{
Enabled = true
};
// add this
ExecuteEvery5Min();
_Timer.Elapsed += (sender, eventArgs) =>
{
ExecuteEvery5Min();
};
If you can, you could use the System.Threading.Timer instead, it has the following constructor:
public Timer (System.Threading.TimerCallback callback, object state, int dueTime, int period);
Quoting from the below link:
dueTime Int32 The amount of time to delay before callback is invoked,
in milliseconds. Specify Infinite to prevent the timer from starting.
Specify zero (0) to start the timer immediately.
period Int32 The time interval between invocations of callback, in
milliseconds. Specify Infinite to disable periodic signaling.
PS: It's callback-based, instead of event-based like the one you're using now.
See: https://learn.microsoft.com/en-us/dotnet/api/system.threading.timer.-ctor?view=netframework-4.8

System thread inside a Timer in Android

So I have a Timer in my Activity and I want to run that every 10 seconds.
I created a System.Threading.Timer:
timer = new Timer ((o) => {
Action syncAct = new Action (async delegate() {
await FetchData();
});
RunOnUiThread (syncAct);
}, null, 0, 10000);
The problem here is that await FetchData() takes longer than 10 seconds and that causes the timer to go on forever. I need the timer to start every time AFTER the sync completes. How can I do that?
Thank you for your time.
Try spawning a thread that sleeps for 10 seconds and runs the code.
Roughly like so:
\\declare doRun as a boolean field
new Thread()
{
public void run()
{
yourClass.doRun = true;
while(yourClass.doRun)
{
fetchData();
try {
sleep(10000);
} catch(InterruptedException e)
break;
}
}
}
}.start();
Set doRun to false to exit the thread.
Or you could assign the thread to a variable instead of an anonymous thread, and call yourThread.interrupt();

Using timers which continue only if the callback has been executed till the end

I'm developing a game server and I need to handle some events. By exemple : a player want to attack another player. If he can, an event is executed every seconds which deals damage.
There's a sample code which is not working, but I hope you will get the idea !
using System.Timers;
public class Test
{
public static Timer FightTimer;
// Session is the player
public static void Main(Session Session)
{
FightTimer = new Timer(1000); // one second interval
// Hook up the Elapsed event for the timer.
FightTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
// Set the Interval to 1 seconds (2000 milliseconds).
FightTimer.Interval = 1000;
FightTimer.Enabled = true;
}
public static void Fight(object attacker)
{
FightTimer.stop();
// get the session
Session Session = (Session)attacker;
if (Session.CharacterInfo.IsDestroy == true)
{
return;
}
// Ok here will be calculated all damage and ect...
// if there's no others "return" for stopping the execution we can let the timer call
// the callback again. if not, the timer is stopped and disposed
FightTimer.start();
}
}
Well I hope you got the idea, my problem is I don't know at all how I can do that so I hope you will be able to help me. Thanks in advance !
Since you are using the System.Timer class you can use the System.Timer.Enabled property. Setting the property to false will stop the timer ticking - it will not raise the Elapsed event anymore.
What need to be changed in your code:
make the timer variable global (or pass it to the desired method)
then use FightTimer.Enabled = false; to stop it
The modified code (one possible solution):
using System.Timers;
public class Test
{
// Session is the player
static Timer FightTimer = null;
public static void Main(Session Session)
{
FightTimer = new Timer(1000); // one second interval
// Hook up the Elapsed event for the timer.
FightTimer.Elapsed += new ElapsedEventHandler(Fight);
// Set the Interval to 1 seconds
FightTimer.Interval = 1000;
FightTimer.Enabled = true;
}
public static void Fight(object attacker)
{
// get the session
Session Session = (Session)attacker;
if (Session.CharacterInfo.IsDestroy == true)
{
return;
}
// Ok here will be calculated all damage and ect...
// if there's no others "return" for stopping the execution we can let the timer call
// the callback again. if not, the timer is stopped and disposed
FightTimer.Enabled = false;
// modify to your needs
}
}

Problem in pausing/resuming Timer

I have a maze game. After you press Enter, you can enter a cheat code, also, the timer will pause. But after entering the code, my timer resumes but it decrements 3x each second. Here's the condition for pressing Enter:
// gt.setTimer() is called at the moment the maze started
// I'm using getch to trap inputs
else if (move == 13) //Reads if Enter is pressed
{
pause = 1; //A Flag saying that the timer must be paused
gt.setTimer(pause); //Calls my setTimer() method
Console.Write("Enter Cheat: ");
cheat = Console.ReadLine();
pause = 0; //A Flag saying that the timer should resume
gt.setTimer(lives, pause); //Calls again the Timer
}
Here's my setTimer() code:
static System.Timers.Timer t = new System.Timers.Timer();
static int gTime = 300;
public void setTimer(int pause)
{
t.Interval = 1000; // Writes the time after every 1 sec
if (pause == 1)
t.Stop(); // Stop the timer if you press Enter
else
t.Start(); // Starts the timer if not
t.Elapsed += new ElapsedEventHandler(showTimer);
}
public static void showTimer(object source, ElapsedEventArgs e)
{
Console.Write("Time " + gTime); //Writes time
gTime--; //Decrements the time
}
Is there something wrong? Am i missing something?
The problem is in the last line of the setTimer method. The timer handler should be registered just once after calling constructor, and not in the setTimer. On the elapsed timer event, the handler is called the number of times it has been registered. Thus the more you use operator += more times it being called.
Every time when you do:
t.Elapsed += new ElapsedEventHandler(showTimer);
you add one more event handler to this event
This strind runing only once, in par code where you initialise timer

Categories

Resources