public class PacketPoller
{
private Timer m_timer;
public void Start()
{
m_timer = new Timer(OnTick(null), null, 0, 1);
m_timer.InitializeLifetimeService();
}
public Action<Object> OnTick() { }
}
This is my code, however, the timer seems to require a staticmethod, rather than an object-oriented method. What can I do about it? I want to user to be able to create a new Timer and then change it's OnTick to set the method to call. How can I do that?
This is what I've successfully used recently.
DispatcherTimer timer1stDoze = new System.Windows.Threading.DispatcherTimer();
timer1stDoze.Tick += new EventHandler(timer1stDoze_Tick);
timer1stDoze.Interval = new TimeSpan(0, 5, 0);
timer1stDoze.Start();
If you're going to stick with the System.Threading.Timer, have it as you have, but have your PacketPoller have its own event that you'll fire in conjunction:
public class PacketPoller
{
public event EventHandler Tick;
private Timer m_timer;
public void Start()
{
m_timer = new Timer(OnTick, null, 0, 1);
m_timer.InitializeLifetimeService();
}
public void OnTick(object state)
{
var tick = this.Tick;
if (tick != null)
tick();
}
}
Listeners will be wired to your PacketPoller.Tick event rather than the m_timer directly. I'm assuming you don't have a state to bubble through, but if you need to you can pass it through. (notice I modified the method signature of OnTick and your constructor call as well)
I'm guessing that you are using the System.Timers.Timer class.
I would recommend trying with the System.Windows.Forms.Timer class instead. It do not require a static callback function and is a bit easier to work with (easier to debug due to it not being threaded).
This will require a reference to the System.Windows.Forms assembly tho.
timer.Tick += new EventHandler(CallbackFunction);
Related
UPDATE: This works in Windows 10 properly.
Here is a simple example:
void testcase()
{
if (myTimer != null)
myTimer.Cancel();
myTimer = ThreadPoolTimer.CreateTimer(
t => myMethod(),
TimeSpan.FromMilliseconds(4000)
);
}
void myMethod()
{
myTimer = null;
//some work
}
What it should do is ensure that myMethod cannot be called more frequent than once in 4s and that myMethod shouldn't be called if there is already a newer call to testcase. Something similar with .net timers on desktop was possible. However, new call to testcase doesn't prevent previously scheduled myMethods from running. I have a simple workaround by adding integer callid parameter to myMethod and keeping track of it. But this above should work and it doesn't.
Am I doing something wrong? Does anyone have also any better idea on how to do this?
What you're looking for is called debouncing, at least in javascript.
A simple way to achieve it is to use the System.Threading.Timer instead, which has a handy Change used to reset it.
If you want to abstract it into your own timer class, it would look something like:
public class DebounceTimer : IDisposable
{
private readonly System.Threading.Timer _timer;
private readonly int _delayInMs;
public DebounceTimer(Action callback, int delayInMs)
{
_delayInMs = delayInMs;
// the timer is initially stopped
_timer = new System.Threading.Timer(
callback: _ => callback(),
state: null,
dueTime: System.Threading.Timeout.Infinite,
period: System.Threading.Timeout.Infinite);
}
public void Reset()
{
// each call to Reset() resets the timer
_timer.Change(
dueTime: _delayInMs,
period: System.Threading.Timeout.Infinite);
}
public void Dispose()
{
// timers should be disposed when you're done using them
_timer.Dispose();
}
}
Your test case would then become:
private DebounceTimer _timer;
void Init()
{
// myMethod will be called 4000ms after the
// last call to _timer.Reset()
_timer = new DebounceTimer(myMethod, 4000);
}
void testcase()
{
_timer.Reset();
}
void myMethod()
{
//some work
}
public void Dispose()
{
// don't forget to cleanup when you're finished testing
_timer.Dispose();
}
[Update]
From your comments, it seems like you'd like to change the callback method with each reset, and only have the last one invoked. If that's the case, you can change the code to something like:
class DebounceTimer : IDisposable
{
private readonly System.Threading.Timer _timer;
private readonly int _delayInMs;
private Action _lastCallback = () => { };
public DebounceTimer(int delayInMs)
{
_delayInMs = delayInMs;
// the timer is initially stopped
_timer = new System.Threading.Timer(
callback: _ => _lastCallback(),
state: null,
dueTime: System.Threading.Timeout.Infinite,
period: System.Threading.Timeout.Infinite);
}
public void Reset(Action callback)
{
_timer.Change(dueTime: _delayInMs, period: System.Threading.Timeout.Infinite);
// note: no thread synchronization is taken into account here,
// a race condition might occur where the same callback would
// be executed twice
_lastCallback = callback;
}
public void Dispose()
{
_timer.Dispose();
}
}
When calling the Reset method, you can use a lambda to capture various method calls (not only Action methods):
void testcase()
{
_timer.Reset(() => myMethod());
}
void othertestcase()
{
// it's still a parameterless action, but it
// calls another method with two parameters
_timer.Reset(() => someOtherMethod(x, y));
}
As stated in the comments for the second timer snippet, the code is not thread safe, because the timer handler may already be running (or just about to run) on a separate thread while the callback reference is being changed inside the Reset method, meaning that the same callback would be executed twice.
A slightly more complex solution would be to lock while changing the callback, and make an additional check if enough time has elapsed since the last call to reset. The final code would then look like this (there might be other ways to synchronize, but this one is pretty straightforward imho):
class DebounceTimer : IDisposable
{
private readonly System.Threading.Timer _timer;
private readonly int _delayInMs;
private readonly object _lock = new object();
private DateTime _lastResetTime = DateTime.MinValue;
private Action _lastCallback = () => { };
public DebounceTimer(int delayInMs)
{
_delayInMs = delayInMs;
// the timer is initially stopped
_timer = new System.Threading.Timer(
callback: _ => InvokeIfTimeElapsed(),
state: null,
dueTime: System.Threading.Timeout.Infinite,
period: System.Threading.Timeout.Infinite);
}
private void InvokeIfTimeElapsed()
{
Action callback;
lock (_lock)
{
// if reset just happened, skip the whole thing
if ((DateTime.UtcNow - _lastResetTime).TotalMilliseconds < _delayInMs)
return;
else
callback = _lastCallback;
}
// if we're here, we are sure we've got the right callback - invoke it.
// (even if reset happens now, we captured the previous callback
// inside the lock)
callback();
}
public void Reset(Action callback)
{
lock (_lock)
{
// reset timer
_timer.Change(
dueTime: _delayInMs,
period: System.Threading.Timeout.Infinite);
// save last reset timestamp
_lastResetTime = DateTime.UtcNow;
// set the new callback
_lastCallback = callback;
}
}
public void Dispose()
{
_timer.Dispose();
}
}
The problem is that you are setting timer = null in myMethod. That guarantees that it will be null in the next call to testCase (so it won't be cancelled).
Instead, use TimerPool.CreateTimer to create a single-instance timer. It will only fire once. When your worker process finishes, the last thing it should do is initialize a new timer.
To answer my self what is likely the problem, it seems that Cancel() is used only to cancel periodic timer from further repeating. I can't say that documentation says exactly that, but it seems that it is working like that. Thus if timer is not periodic like in this case, Cancel has no effect.
UPDATE: this works in Windows 10 as it should.
I have a windows form with a button.
I click the button and it starts a method in a separate class. I start this method in a separate thread.
When this class.method finishes it raises an event back to the windows form class.
When this happens I start another method in that separate class that tells a system.windows.form timer (declared in that class) to be enabled and thus start processing.
But the timer does not start (I did put a break point inside the 'tick' event).
I am assuming that it is because I declared the timer outside of the calling thread right at the start of my code.
Normally, I would use this to invoke a method on the same thread...
this.invoke(mydelegatename, any pars);
But, 'this' cannot be called with an class because unassumingly it is related to the UI thread.
I know this all looks bad architecture and I can easily solve this problem by moving the timer to the UI thread (windows form class).
But, I have forgotten how I did this many years ago and it really is an attempt to encapsulate my code.
Can anyone enlighten me pls?
Thanks
The Code:
[windows class]
_webSync = new WebSync(Shared.ClientID);
_webSync.evBeginSync += new WebSync.delBeginSync(_webSync_evBeginSync);
Thread _thSync = new Thread(_webSync.PreConnect);
_thSync.Start();
private void _webSync_evBeginSync()
{
_webSync.Connect();
}
[WebSync class]
private System.Windows.Forms.Timer _tmrManifestHandler = new System.Windows.Forms.Timer();
public WebSyn()
{
_tmrManifestHandler.Tick += new EventHandler(_tmrManifestHandler_Tick);
_tmrManifestHandler.Interval = 100;
_tmrManifestHandler.Enabled = false;
}
public delegate void delBeginSync();
public event delBeginSync evBeginSync;
public void PreConnect()
{
while (true)
{
if (some condition met)
{
evBeginSync();
return ;
}
}
}
public void Connect()
{
_tmrManifestHandler.Enabled = true;
_tmrManifestHandler.Start();
}
private void _tmrManifestHandler_Tick(object sender, EventArgs e)
{
//NOT BEING 'HIT'
}
You have to call _tmrManifestHandler.Start(); enabling is not enough.
Using a System.Windows.Forms.Timer on another thread will not work.
for more info look here.
Use a System.Timers.Timer instead, be carefull of CrossThreadExceptions if you are using accessing UI elements.
public class WebSync
{
private System.Timers.Timer _tmrManifestHandler = new System.Timers.Timer();
public WebSync(object id)
{
_tmrManifestHandler.Elapsed += new System.Timers.ElapsedEventHandler(_tmrManifestHandler_Tick);
_tmrManifestHandler.Interval = 100;
_tmrManifestHandler.Enabled = false;
}
public delegate void delBeginSync();
public event delBeginSync evBeginSync;
public void PreConnect()
{
while (true)
{
if (true /* just for testing*/)
{
evBeginSync();
return;
}
}
}
public void Connect()
{
_tmrManifestHandler.Enabled = true;
_tmrManifestHandler.Start();
}
private void _tmrManifestHandler_Tick(object sender, EventArgs e)
{
//NOT BEING 'HIT'
}
}
I have my winforms built in timer:
System.Windows.Forms.Timer timerLoop;
When the timer is started, I want (if possible) to pass this timer an integer value.
timerLoop.Start();
Till now, I only created a general variable that the timer can read and update this variable just before starting my timer.
You can do this two ways (maybe more):
Extend base Timer by creating new one that inherit it:
private class TimerExnteded : Timer
{
public int Value { get; set; }
public TimerExnteded(int value)
{
Value = value;
}
}
and use that value in Tick event.
Use Tag property of Timer
Timer t = new Timer();
t.Tag = 5;
t.Start();
//event
private void t_Tick(object sender, EventArgs e)
{
var timer = sender as Timer;
var value = (timer.Tag as int?) ?? 0;
value++;
timer.Tag = value;
}
Second approach uses boxing/unboxing of value.
You can also use closure:
t.Tick += (s, a) => OnTick(YourValue);
I am really stuck with how to use events in my app. I have two files:
RTimer.cs
SettingsForm.cs
I have a timer set up in RTimer.cs that initializes the timer and sets the interval and such.
I have a method in SettingsForm.cs that needs to do something every time the timer ticks. This file also has a method that sets the timers interval via a SetTimer() method in the RTimer.cs
I cant for the life of me figure out how to get the Tick event to call the method from the other class, or get the method in the other class to subscribe to the Tick event.
You can implement this way :
class SettingsForm
{
public void OnTimerEvent(object sender, EventArgs e)
{
throw new NotImplementedException();
}
}
class RTimer
{
Timer timer = new Timer();
public void StartTimer(SettingsForm settingForm)
{
timer.Tick += settingForm.OnTimerEvent;
timer.Interval = 5000;
timer.Enabled = true;
}
}
ive been working on a program. i has 3 classes. 2 of the classes have timers that repeat at different intervals and once one "cycle" of the timer is done it raises an event with a string as return. the 3rd class subscribes to the events from the other two timer classes and prints them to screen. it works great!
but my issue is that it prints them separately. say currently the first timer class runs and then raises "hello" every 2 minutes and the other class "dog" every second. then every time an event is raised it prints the raised event to console. i would want it to instead print "hellodog" every second.
i was thinking: so each time a timer fires it will raise an event and update a string in the "output" class with the current value, then make another timer that goes off every second, this timer will read both the updated strings together as one output like "hellodog". is this possible if it is this is the easiest way i think. how would i achieve this idea?
if it is confusing i will clarify.
namespace Final
{
public class Output
{
public static void Main()
{
var timer1 = new FormWithTimer();
var timer2 = new FormWithTimer2();
timer1.NewStringAvailable += new EventHandler<BaseClassThatCanRaiseEvent.StringEventArgs>(timer1_NewStringAvailable);
timer2.NewStringAvailable += new EventHandler<BaseClassThatCanRaiseEvent.StringEventArgs>(timer2_NewStringAvailable);
Console.ReadLine();
}
static void timer1_NewStringAvailable(object sender, BaseClassThatCanRaiseEvent.StringEventArgs e)
{
var theString = e.Value;
//To something with 'theString' that came from timer 1
Console.WriteLine(theString);
}
static void timer2_NewStringAvailable(object sender, BaseClassThatCanRaiseEvent.StringEventArgs e)
{
var theString2 = e.Value;
//To something with 'theString2' that came from timer 2
Console.WriteLine(theString2);
}
}
public abstract class BaseClassThatCanRaiseEvent
{
public class StringEventArgs : EventArgs
{
public StringEventArgs(string value)
{
Value = value;
}
public string Value { get; private set; }
}
//The event itself that people can subscribe to
public event EventHandler<StringEventArgs> NewStringAvailable;
protected void RaiseEvent(string value)
{
var e = NewStringAvailable;
if (e != null)
e(this, new StringEventArgs(value));
}
}
public partial class FormWithTimer : BaseClassThatCanRaiseEvent
{
Timer timer = new Timer();
public FormWithTimer()
{
timer = new System.Timers.Timer(200000);
timer.Elapsed += new ElapsedEventHandler(timer_Tick); // Everytime timer ticks, timer_Tick will be called
timer.Interval = (200000); // Timer will tick evert 10 seconds
timer.Enabled = true; // Enable the timer
timer.Start(); // Start the timer
}
void timer_Tick(object sender, EventArgs e)
{
...
RaiseEvent(gml.ToString());
}
}
public partial class FormWithTimer2 : BaseClassThatCanRaiseEvent
{
Timer timer = new Timer();
public FormWithTimer2()
{
timer = new System.Timers.Timer(1000);
timer.Elapsed += new ElapsedEventHandler(timer_Tick2); // Everytime timer ticks, timer_Tick will be called
timer.Interval = (1000); // Timer will tick evert 10 seconds
timer.Enabled = true; // Enable the timer
timer.Start(); // Start the timer
}
void timer_Tick2(object sender, EventArgs e)
{
...
RaiseEvent(aida.ToString());
}
}
}
You can use the same event handler for both timers. And construct the output by identifying the senders. (Didn't test the code for syntax errors.)
private static string timer1Value = string.Empty;
private static string timer2Value = string.Empty;
private static FormWithTimer timer1;
private static FormWithTimer2 timer2;
public static void Main()
{
timer1 = new FormWithTimer();
timer2 = new FormWithTimer2();
timer1.NewStringAvailable += new EventHandler<BaseClassThatCanRaiseEvent.StringEventArgs>(timer1_NewStringAvailable);
timer2.NewStringAvailable += new EventHandler<BaseClassThatCanRaiseEvent.StringEventArgs>(timer1_NewStringAvailable);
Console.ReadLine();
}
static void timer1_NewStringAvailable(object sender, BaseClassThatCanRaiseEvent.StringEventArgs e)
{
if (sender == timer1)
{
timer1Value = e.Value.ToString();
}
else if (sender == timer2)
{
timer2Value = e.Value.ToString();
}
if (timer1Value != String.Empty && timer2Value != String.Empty)
{
Console.WriteLine(timer1Value + timer2Value);
// Do the string concatenation as you want.
}
When the events are handled in your example they have no access to information about the other events. If you want to have 2 events that update strings, but you want the handler to print data from both updated strings, you need the event handlers to have access to both of those strings. You can either store them in variables on the event handling class, or make them public properties of the classes that are raising the events. That way in either event handler you have access to the updated strings from other events.