System.Timers.Timer - How to change timer intervals on the fly? - c#

I'm making a service. Among other things, it must read the value from registry and configure "Primary" timers' interval according to this value. Now it should do it not only in constructor phase, but also be able to change this interval when it has been changed in registry. How do I do that?
In other words, I have a code like this:
using System.Timers;
public static class GVARS
{
public static Config valFromRegistry = new ConfigRegistry().Result;
}
class Program
{
static void Main(string[] args)
{
int timerPeriod = (int)(uint)GVARS.valFromRegistry.Interval * 60000;
Timer primaryTimer = new Timer(timerPeriod);
primaryTimer.Interval = timerPeriod;
primaryTimer.Elapsed += RunPrimaryPayload;
primaryTimer.Start();
Console.ReadLine();
}
private static void RunPrimaryPayload(object sender, ElapsedEventArgs eventArgs)
{
SomeClass.SomeMethod(someArgs);
GVARS.valFromRegistry.Interval = (int)someNewValue;
}
}
Somewhere down the code I might change valFromRegistry.Interval and I want primaryTimer to respond to those changes. How do I get there?
PS. It's not really important to me how I'm gonna check for changes in registry, be it another timer or event. Whatever works.
PS2. I have a possible idea to use System.Threading.Timer for primaryTimer instead of System.Timers.Timer since the former has method Change() which I might call from another little configrationUpdateTimer every minute or so and make primaryTimer respond to configuration changes this way. Am I right?

Ok, I've managed to solve my problem by creating class with 2 Timers: one checking for config changes (master) and another (slave) is doing actual payload:
public class UniversalTimer
{
public System.Threading.Timer MasterTimer;
public System.Threading.Timer SlaveTimer;
private int lastMainTimerInterval = 0;
public UniversalTimer()
{
MasterTimer = new System.Threading.Timer(new System.Threading.TimerCallback(MasterTimerCallback), null, 0, 60000);
SlaveTimer = new System.Threading.Timer(new System.Threading.TimerCallback(SlaveTimerCallback), null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
}
private void MasterTimerCallback(object obj)
{
Log.Debug("[{0}:{1}:{2}] Re-reading configuration from registry", "Program", "UniversalTimer", "MasterTimerCallback");
var currentSettings = new ConfigRegistry().Result;
if ((int)currentSettings.Interval != lastMainTimerInterval)
{
Log.Debug("[{0}:{1}:{2}] Setting interval for Main Timer", "Program", "UniversalTimer", "SlaveTimerCallback");
lastMainTimerInterval = (int)currentSettings.Interval;
this.MainTimer.Change(0, (int)currentSettings.Interval * 60000);
}
}
private void SlaveTimerCallback(object obj)
{
Log.Debug("[{0}:{1}:{2}] Executing main cycle", "Program", "UniversalTimer", "SlaveTimerCallback");
}
public void Dispose()
{
MasterTimer.Dispose();
SlaveTimer.Dispose();
}
}

Related

C# simple stopwatch - Total beginner

I come from embedded C programming and I don't absolutely know anything about oop, task, threads, ecc..
I'm figuring out the basics concepts of C# and oop and wanted to try coding a simple stopwatch.
Goal: Toggle the stopwatch state with a button and show the value inside the button itself.
So far I've come to this:
namespace Cronometro
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MyTimerClass
{
private DateTime TimeValue;
private System.Timers.Timer TimerObj;
private const int TimeInterval = 1;
public MyTimerClass()
{
TimeValue new DateTime(0, 0, 0, 0, 0, 0, 0, DateTimeKind.Local);
TimerObj = new System.Timers.Timer(TimeInterval);
TimerObj.AutoReset = true;
TimerObj.Elapsed += TimerObj_Elapsed;
}
private void TimerObj_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
TimeValue = TimeValue.AddMilliseconds((double)TimeInterval);
}
public void Start()
{
TimerObj.Start();
}
public void Stop()
{
TimerObj.Stop();
}
}
}
Come to this point, I just wanted to print the value with Button.Content but seems like I cannot use it outside of the MainWindow class.
What should I do? I started thinking about creating a task inside the main class and wait for the timer event to print the value but I know nothing about threads and tasks.
It's unclear exactly what you mean, as at the moment you can't use it in the MainWindow class at all. Just expose the value via a public function, and you can fetch the instance's value anywhere you want.
namespace Cronometro
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var x = new MyTimerClass(); // <<<
x.Start(); // <<<
var timeValue = x.GetTimeValue(); // <<<
}
}
public class MyTimerClass
{
private DateTime TimeValue;
private System.Timers.Timer TimerObj;
private const int TimeInterval = 1;
public MyTimerClass()
{
TimeValue new DateTime(0, 0, 0, 0, 0, 0, 0, DateTimeKind.Local);
TimerObj = new System.Timers.Timer(TimeInterval);
TimerObj.AutoReset = true;
TimerObj.Elapsed += TimerObj_Elapsed;
}
private void TimerObj_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
TimeValue = TimeValue.AddMilliseconds((double)TimeInterval);
}
public DateTime GetTimeValue() // <<<
{ // <<<
return TimeValue; // <<<
} // <<<
public void Start()
{
TimerObj.Start();
}
public void Stop()
{
TimerObj.Stop();
}
}
First, you don't have to write your own Stopwatch class. DotNet has its own:
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch?view=netframework-4.8
Second, try to understand async/await pattern:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/
So, you can do something like this:
https://wouterdekort.com/2013/10/01/async-and-await-and-the-ui-thread/
One way you could populate the button text is you create an event in the MyTimerClass class, and attach a listener delegate to it from inside the MainWindow class.
Then, fire the event in TimerObj_Elapsed in MyTimerClass, so that the event handler in MainWindow will be called, and you will have a chance to populate the button.
However, there is something other than "practicing class" you have to deal with. Because you are using a thread pool timer, so the Elapsed event handler of the timer is invoked from a worker thread. In a GUI app, only the main / UI thread can interactive with controls. So chances are, you will get an exception when trying to set Button.Text. You will need to marshal the set action to the UI thread.
Frankly, if you are practicing writing classes in C#, I'd recommend you using a Console App to start with rather than a GUI app. It is probably not a good idea if people throwing out too many concepts on the table that you don't know at the same time.

Detect Duplicate Items in DataFlow

I've been building out a service that processes files using a Queue<string> object to manage the items.
public partial class BasicQueueService : ServiceBase
{
private readonly EventWaitHandle completeHandle =
new EventWaitHandle(false, EventResetMode.ManualReset, "ThreadCompleters");
public BasicQueueService()
{
QueueManager = new Queue<string>();
}
public bool Stopping { get; set; }
private Queue<string> QueueManager { get; }
protected override void OnStart(string[] args)
{
Stopping = false;
ProcessFiles();
}
protected override void OnStop()
{
Stopping = true;
}
private void ProcessFiles()
{
while (!Stopping)
{
var count = QueueManager.Count;
for (var i = 0; i < count; i++)
{
//Check the Stopping Variable again.
if (Stopping) break;
var fileName = QueueManager.Dequeue();
if (string.IsNullOrWhiteSpace(fileName) || !File.Exists(fileName))
continue;
Console.WriteLine($"Processing {fileName}");
Task.Run(() =>
{
DoWork(fileName);
})
.ContinueWith(ThreadComplete);
}
if (Stopping) continue;
Console.WriteLine("Waiting for thread to finish, or 1 minute.");
completeHandle.WaitOne(new TimeSpan(0, 0, 15));
completeHandle.Reset();
}
}
partial void DoWork(string fileName);
private void ThreadComplete(Task task)
{
completeHandle.Set();
}
public void AddToQueue(string file)
{
//Called by FileWatcher/Manual classes, not included for brevity.
lock (QueueManager)
{
if (QueueManager.Contains(file)) return;
QueueManager.Enqueue(file);
}
}
}
Whilst researching how to limit the number of threads on this (I've tried a manual class with an incrementing int, but there's an issue where it doesn't decrement properly in my code), I came across TPL DataFlow, which seems like its a better fit for what I'm trying to achieve - specifically, it allows me to let the framework handle threading/queueing, etc.
This is now my service:
public partial class BasicDataFlowService : ServiceBase
{
private readonly ActionBlock<string> workerBlock;
public BasicDataFlowService()
{
workerBlock = new ActionBlock<string>(file => DoWork(file), new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 32
});
}
public bool Stopping { get; set; }
protected override void OnStart(string[] args)
{
Stopping = false;
}
protected override void OnStop()
{
Stopping = true;
}
partial void DoWork(string fileName);
private void AddToDataFlow(string file)
{
workerBlock.Post(file);
}
}
This works well. However, I want to ensure that a file is only ever added to the TPL DataFlow once. With the Queue, I can check that using .Contains(). Is there a mechanism that I can use for TPL DataFlow?
Your solution with Queue works only if file goes into your service twice in a small period of time. If it came again in, say, few hours, queue will not contain it, as you Dequeue it from there.
If this solution is expected, then you may use a MemoryCache to store file paths being already handled, like this:
using System.Runtime.Caching;
private static object _lock = new object();
private void AddToDataFlow(string file)
{
lock (_lock)
{
if (MemoryCache.Default.Contains(file))
{
return;
}
// no matter what to put into the cache
MemoryCache.Default[file] = true;
// we can now exit the lock
}
workerBlock.Post(file);
}
However, if your application must run for a long time (which service is intended to do), you'll eventually run out of memory. In that case you probably need to store your file paths in database or something, so even after restarting the service your code will restore the state.
You can check it inside of DoWork.
You have to save in Hash already works items and check current filename doesn't exist in hash.

How to set up a application-wide Timer?

I want to have a timer in my windows phone 8 app, that´s counting/running independent of current shown page.
It should connect to server - when possible in a UI independet task/thread - and store data in a global object/list.
The Independence from current shown page is my point.
I tried following in App.xaml.cs:
public partial class App : Application
{
// creating timer instance
DispatcherTimer gAppTimer = new DispatcherTimer();
// timer interval specified as 1 minute
gAppTimer.Interval = TimeSpan.FromSeconds(60);
// Sub-routine OnTimerTick that will be called at specified intervall
gAppTimer.Tick += OnTimerTick;
// starting the timer
gAppTimer.Start();
public void OnTimerTick(Object sender, EventArgs args)
{
// text box property is set to current system date.
// ToString() converts the datetime value into text
MessageBox.Show("TIMER fired");
}
:
:
But this doesn´t work. Than I tried just declaring the object in App.xaml.cs:
public partial class App : Application
{
// creating timer instance
DispatcherTimer gAppTimer = new DispatcherTimer();
public void OnTimerTick(Object sender, EventArgs args)
{
// text box property is set to current system date.
// ToString() converts the datetime value into text
MessageBox.Show("TIMER fired");
}
:
:
And on my startpage.xaml.cs:
// timer interval specified as 1 minute
App.gAppTimer.Interval = TimeSpan.FromSeconds(60);
// Sub-routine OnTimerTick that will be called at specified intervall
App.gAppTimer.Tick += OnTimerTick;
// starting the timer
App.gAppTimer.Start();
But this doesn´t work, too.
Any ideas how to handle my Problem? What I don´t want to use is a Background Task, because it runs only every 30 minutes. My solution should only run, if the app is "active" (in foreground).
That's normally done using a static or singleton class. Both will be global and you'll have access to them from every page.
Also, the DispatcherTimer invokes it's TimerTick method on the UI thread. If you don't need to be in the UI thread, you should use a System.Threading.Timer, which invokes a method in a background thread.
Here's an example:
public static class SomeManager {
private static Timer gAppTimer;
private static object lockObject = new object();
public static void StartTimer() {
if (gAppTimer == null) {
lock (lockObject) {
if (gAppTimer == null) {
gAppTimer = new Timer(OnTimerTick, null, 60 * 1000, 60 * 1000);
}
}
}
}
public static void StopTimer() {
if (gAppTimer != null) {
lock (lockObject) {
if (gAppTimer != null) {
gAppTimer.Change(Timeout.Infinite, Timeout.Infinite);
gAppTimer = null;
}
}
}
}
private static void OnTimerTick(object state) {
Action();
}
public static void Action() {
// Do what you need to do
}
}
Just call SomeManager.StartTimer() from your first page or from App.xaml.cs and the timer will start.
Update
I updated the code a little:
Renamed the Initialize method to StartTimer.
Added StopTimer method which stops the timer. You can then start it again by calling SomeManager.StartTimer.
Added Action method which is the one actually donig the work. You can invoke it from anywhere, anytime.
Note: the the timer will call this method in a background thread and you should do the same using something like Task.Run(() => SomeManager.Action());
Added a lock to ensure that the Start/Stop methods will not throw exceptions if invoked from multiple threads at the same time.
I'm not sure how you have arranged your code, but as I've tried:
public partial class App : Application
{
public static PhoneApplicationFrame RootFrame { get; private set; }
public DispatcherTimer gAppTimer = new DispatcherTimer();
public void OnTimerTick(Object sender, EventArgs args)
{
MessageBox.Show("TIMER fired");
}
public App()
{
gAppTimer.Interval = TimeSpan.FromSeconds(2);
// Sub-routine OnTimerTick that will be called at specified intervall
gAppTimer.Tick += OnTimerTick;
// starting the timer
gAppTimer.Start();
// rest of the code
the above code works. MessageBox shows every 2 seconds, if you had declared your DispatcherTimer as public, then you will be able to access it like this:
(App.Current as App).gAppTimer.Stop();
Note also that depending on what you want to achieve you may also use System.Threading.Timer.
On the other hand you may also think of using public static DispatcherTimer somewhere.

C# Getting return value from Elapsed Time Event

Hi I have a similar problem to an old thread that I still can't work around
(Pass a return value back through an EventHandler) . I am eventually trying to implement a timer that will calculate velocity at even intervals so I require the Elapsed Time Event to return some kind of value. I've tried using global variables but the event doesn't seem to change the variable. Any advice? Thanks in advance!
namespace Timer_Label
{
public static class GlobalVariables
{
public static int _stringHolder;
public static int StringHolder
{
get { return _stringHolder; }
set { _stringHolder = value; }
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
System.Timers.Timer myTimer = new System.Timers.Timer();
myTimer.Elapsed += new ElapsedEventHandler(DisplayTimeEvent);
myTimer.Interval = 500;
myTimer.Start();
MessageBox.Show(Convert.ToString(GlobalVariables.StringHolder));
}
public static void DisplayTimeEvent(object sender, ElapsedEventArgs e)
{
GlobalVariables.StringHolder = "1";
}
It's highly improbable that the timer will have expired by the time the code reaches your MessageBox.Show statement.
Try placing a Thread.Sleep(1000) call between myTimer.Start() and MessageBox.Show(). This should give the timer time to elapse and execute your DisplayTimeEvent handler.
Eventually, you will need to take into account the fact that the System.Timer.Timer class executes its callback in a background thread, so changes that you make in DisplayTimeEvent will need to be synchronized.

Alarm clock application in .Net

I'm not really writing an alarm clock application, but it will help to illustrate my question.
Let's say that I have a method in my application, and I want this method to be called every hour on the hour (e.g. at 7:00 PM, 8:00 PM, 9:00 PM etc.). I could create a Timer and set its Interval to 3600000, but eventually this would drift out of sync with the system clock. Or I could use a while() loop with Thread.Sleep(n) to periodically check the system time and call the method when the desired time is reached, but I don't like this either (Thread.Sleep(n) is a big code smell for me).
What I'm looking for is some method in .Net that lets me pass in a future DateTime object and a method delegate or event handler, but I haven't been able to find any such thing. I suspect there's a method in the Win32 API that does this, but I haven't been able to find that, either.
Or, you could create a timer with an interval of 1 second and check the current time every second until the event time is reached, if so, you raise your event.
You can make a simple wrapper for that :
public class AlarmClock
{
public AlarmClock(DateTime alarmTime)
{
this.alarmTime = alarmTime;
timer = new Timer();
timer.Elapsed += timer_Elapsed;
timer.Interval = 1000;
timer.Start();
enabled = true;
}
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
if(enabled && DateTime.Now > alarmTime)
{
enabled = false;
OnAlarm();
timer.Stop();
}
}
protected virtual void OnAlarm()
{
if(alarmEvent != null)
alarmEvent(this, EventArgs.Empty);
}
public event EventHandler Alarm
{
add { alarmEvent += value; }
remove { alarmEvent -= value; }
}
private EventHandler alarmEvent;
private Timer timer;
private DateTime alarmTime;
private bool enabled;
}
Usage:
AlarmClock clock = new AlarmClock(someFutureTime);
clock.Alarm += (sender, e) => MessageBox.Show("Wake up!");
Please note the code above is very sketchy and not thread safe.
Interesting, I've actually come across a very similar issue and went looking for a method in the .Net framework that would handle this scenario. In the end, we ended up implementing our own solution that was a variation on a while loop w/ Thread.Sleep(n) where n gets smaller the closer you get to the desired target time (logarithmically actually, but with some reasonable thresholds so you're not maxing the cpu when you get close to the target time.) Here's a really simple implementation that just sleeps half the time between now and the target time.
class Program
{
static void Main(string[] args)
{
SleepToTarget Temp = new SleepToTarget(DateTime.Now.AddSeconds(30),Done);
Temp.Start();
Console.ReadLine();
}
static void Done()
{
Console.WriteLine("Done");
}
}
class SleepToTarget
{
private DateTime TargetTime;
private Action MyAction;
private const int MinSleepMilliseconds = 250;
public SleepToTarget(DateTime TargetTime,Action MyAction)
{
this.TargetTime = TargetTime;
this.MyAction = MyAction;
}
public void Start()
{
new Thread(new ThreadStart(ProcessTimer)).Start();
}
private void ProcessTimer()
{
DateTime Now = DateTime.Now;
while (Now < TargetTime)
{
int SleepMilliseconds = (int) Math.Round((TargetTime - Now).TotalMilliseconds / 2);
Console.WriteLine(SleepMilliseconds);
Thread.Sleep(SleepMilliseconds > MinSleepMilliseconds ? SleepMilliseconds : MinSleepMilliseconds);
Now = DateTime.Now;
}
MyAction();
}
}
You could simply reset the timer duration each time it fires, like this:
// using System.Timers;
private void myMethod()
{
var timer = new Timer {
AutoReset = false, Interval = getMillisecondsToNextAlarm() };
timer.Elapsed += (src, args) =>
{
// Do timer handling here.
timer.Interval = getMillisecondsToNextAlarm();
timer.Start();
};
timer.Start();
}
private double getMillisecondsToNextAlarm()
{
// This is an example of making the alarm go off at every "o'clock"
var now = DateTime.Now;
var inOneHour = now.AddHours(1.0);
var roundedNextHour = new DateTime(
inOneHour.Year, inOneHour.Month, inOneHour.Day, inOneHour.Hour, 0, 0);
return (roundedNextHour - now).TotalMilliseconds;
}
You could create an Alarm class which has a dedicated thread which goes to sleep until the specified time, but this will use the Thread.Sleep method. Something like:
/// <summary>
/// Alarm Class
/// </summary>
public class Alarm
{
private TimeSpan wakeupTime;
public Alarm(TimeSpan WakeUpTime)
{
this.wakeupTime = WakeUpTime;
System.Threading.Thread t = new System.Threading.Thread(TimerThread) { IsBackground = true, Name = "Alarm" };
t.Start();
}
/// <summary>
/// Alarm Event
/// </summary>
public event EventHandler AlarmEvent = delegate { };
private void TimerThread()
{
DateTime nextWakeUp = DateTime.Today + wakeupTime;
if (nextWakeUp < DateTime.Now) nextWakeUp = nextWakeUp.AddDays(1.0);
while (true)
{
TimeSpan ts = nextWakeUp.Subtract(DateTime.Now);
System.Threading.Thread.Sleep((int)ts.TotalMilliseconds);
try { AlarmEvent(this, EventArgs.Empty); }
catch { }
nextWakeUp = nextWakeUp.AddDays(1.0);
}
}
}
I know it's a bit of an old question, but I came across this when I was looking for an answer to something else. I thought I'd throw my two cents in here, since I recently had this particular issue.
Another thing you can do is schedule the method like so:
/// Schedule the given action for the given time.
public async void ScheduleAction ( Action action , DateTime ExecutionTime )
{
try
{
await Task.Delay ( ( int ) ExecutionTime.Subtract ( DateTime.Now ).TotalMilliseconds );
action ( );
}
catch ( Exception )
{
// Something went wrong
}
}
Bearing in mind it can only wait up to the maximum value of int 32 (somewhere around a month), it should work for your purposes. Usage:
void MethodToRun ( )
{
Console.WriteLine ("Hello, World!");
}
void CallingMethod ( )
{
var NextRunTime = DateTime.Now.AddHours(1);
ScheduleAction ( MethodToRun, NextRunTime );
}
And you should have a console message in an hour.
What about System.Timers.Timer class ? See http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx
I have used this before with great success:
Vb.net:
Imports System.Threading
Public Class AlarmClock
Public startTime As Integer = TimeOfDay.Hour
Public interval As Integer = 1
Public Event SoundAlarm()
Public Sub CheckTime()
While TimeOfDay.Hour < startTime + interval
Application.DoEvents()
End While
RaiseEvent SoundAlarm()
End Sub
Public Sub StartClock()
Dim clockthread As Thread = New Thread(AddressOf CheckTime)
clockthread.Start()
End Sub
End Class
C#:
using System.Threading;
public class AlarmClock
{
public int startTime = TimeOfDay.Hour;
public int interval = 1;
public event SoundAlarmEventHandler SoundAlarm;
public delegate void SoundAlarmEventHandler();
public void CheckTime()
{
while (TimeOfDay.Hour < startTime + interval) {
Application.DoEvents();
}
if (SoundAlarm != null) {
SoundAlarm();
}
}
public void StartClock()
{
Thread clockthread = new Thread(CheckTime);
clockthread.Start();
}
}
I don't know if the c# works, but the vb works just fine.
Usage in VB:
Dim clock As New AlarmClock
clock.interval = 1 'Interval is in hours, could easily convert to anything else
clock.StartClock()
Then, just add an event handler for the SoundAlarm event.

Categories