So, i have this piece of code:
public void MovementTick()
{
if(MovementBehaviour != null)
{
MovementBehaviour.PerformMovement(this);
GameScheduler.Schedule(new SchedulerTask(MovementDelay)
{
Task = () =>
{
MovementTick();
}
});
}
}
This is to keep the scheduler running the monster tasks. However, since this is a nested call on MovementTick() will this cause memory problems somehow ? Is this code safe to use ? Should this be calling outside functions not to nest the MovementTick method ?
Would apreciate any help
Try using a timer tick event.
Timer timer { get; set; }
void Start ()
{
timer = new Timer();
timer.Interval = Timespan.FromMilliseconds(100);
timer.Elapsed +=TimerTick;
timer.Start();
}
void TimerTick(object o, EventArgs e)
{
MovementBehaviour?.PerformMovement(this);
}
Related
I am new to C# and try to create a setInterval and clearInterval function which is exactly working like the same functions in javascript.
I do it mainly for practice and to learn what can be done in C# and what not.
setInterval
Requirements: Create a new timer and return it, also run a anonymous function or a predefined function again and again in the given interval.
Timer setInterval(Func<int> myMethod, int intervalInMs)
{
var timer = new Timer();
timer.Start();
while (true) { //probably a infinite loop
if (timer.ElapsedMilliseconds >= intervalInMs)
{
myMethod();
timer.Restart();
}
}
return timer; //Code does never reach this part obviously because of the while loop
}
clearInterval
Requirements: Stop the timer.
void clearInterval(Timer timer)
{
timer.Stop();
}
Planned Usage
Timer myTimer = setInterval(delegate {
MessageBox.Show(
"test",
"test",
MessageBoxButtons.OK,
MessageBoxIcon.Warning
);
return 1;
}, 5000);
//somewhere in code...
clearInterval(myTimer);
How is it possible to solve this with C#, by using Events?
The .Net framework provides at least three different timers - System.Timers.Timer, System.Threading.Timer and System.Windows.Forms.Timer.
The System.Diagnostic.Stopwatch is not a timer and should not be used as one. It only measures how much time have passed between the Start() and Stop().
I would suggest using one of the timers provided by the .Net framework instead of re-inventing the wheel.
update
Well, since you insisted,
Here is a simple implementation NOT FOR USE IN PRODUCTION CODE since it's very easy to create memory leaks with it:
public static class Interval
{
public static System.Timers.Timer Set(System.Action action, int interval)
{
var timer = new System.Timers.Timer(interval);
timer.Elapsed += (s, e) => {
timer.Enabled = false;
action();
timer.Enabled = true;
};
timer.Enabled = true;
return timer;
}
public static void Stop(System.Timers.Timer timer)
{
timer.Stop();
timer.Dispose();
}
}
You can see a live demo on rextester.
I managed to solve it by using events. It is maybe not the best solution, but as I already mentioned, I am a C# beginner.
Example:
This example shows a message box every 5000 ms, after three Messageboxes the interval is changed and at 10 boxes the interval is cleared. You can also stop the interval by pressing the button.
using System;
using System.Windows.Forms;
namespace Intervals
{
public partial class Form1 : Form
{
Interval ival1 = new Interval();
private int _A;
public int A
{
get { return _A; }
set {
_A = value;
if (value == 3) {
if (ival1 != null) {
MessageBox.Show(
"changeInterval Triggered",
A.ToString(),
MessageBoxButtons.OK,
MessageBoxIcon.Warning
);
ival1.changeInterval(1000);
}
}
if (value >= 10)
{
if (ival1 != null)
{
MessageBox.Show(
"clearInterval Triggered",
A.ToString(),
MessageBoxButtons.OK,
MessageBoxIcon.Warning
);
ival1.clearInterval();
}
}
}
}
public Form1()
{
InitializeComponent();
int interval = 5000;
ival1.setInterval(delegate {
A++;
MessageBox.Show(
A.ToString(),
"A",
MessageBoxButtons.OK,
MessageBoxIcon.Warning
);
}, interval);
}
private void stopInterval_Click(object sender, EventArgs e)
{
ival1.clearInterval();
}
}
}
I created a new class Interval which is needed in order for this program to work.
using System;
namespace Intervals
{
public class Interval
{
private System.Timers.Timer timer;
private Action main;
public Interval()
{
timer = new System.Timers.Timer();
}
public void setInterval(Action pAction, int interval)
{
if (interval <= 0) { interval = 100; }
timer.Interval = interval;
main = new Action(delegate{
timer.Stop();
pAction?.Invoke();
timer.Start();
});
timer.Elapsed += (sender, e) => TimerEventProcessor(sender, e);
timer.Start();
}
public void changeInterval(int interval)
{
timer.Stop();
timer.Interval = interval;
timer.Start();
}
public void clearInterval()
{
main?.EndInvoke(null);
main = delegate
{
timer.Stop();
timer.Dispose();
};
}
public void TimerEventProcessor(Object myObject, EventArgs myEventArgs)
{
main?.Invoke();
}
}
}
Memory Usage Test (260 Minutes):
I have a event in my code that can possibly get fired multiple times a second at some moment.
However I would like to implement a way to make that method wait 500ms before really firing, if the method gets called again before those 500ms are over, reset the timer and wait for 500ms again.
Coming from javascript I know this is possible with setTimeout or setInterval. However I'm having trouble figuring out how I could implement such a thing in C#.
You could use a System.Timers.Timer wrapped in a class to get the behaviour you need:
public class DelayedMethodCaller
{
int _delay;
Timer _timer = new Timer();
public DelayedMethodCaller(int delay)
{
_delay = delay;
}
public void CallMethod(Action action)
{
if (!_timer.Enabled)
{
_timer = new Timer(_delay)
{
AutoReset = false
};
_timer.Elapsed += (object sender, ElapsedEventArgs e) =>
{
action();
};
_timer.Start();
}
else
{
_timer.Stop();
_timer.Start();
}
}
}
This can then be used in the following manner:
public class Program
{
static void HelloWorld(int i)
{
Console.WriteLine("Hello World! " + i);
}
public static void Main(string[] args)
{
DelayedMethodCaller methodCaller = new DelayedMethodCaller(500);
methodCaller.CallMethod(() => HelloWorld(123));
methodCaller.CallMethod(() => HelloWorld(123));
while (true)
;
}
}
If you run the example, you will note that "Hello World! 123" is only displayed once - the second call simply resets the timer.
If you need to reset the timer when the method is called again, consider looking at the ManualResetEvent class:
https://msdn.microsoft.com/en-us/library/system.threading.manualresetevent(v=vs.110).aspx
You can use this to notify one or more waiting threads that an event has occurred.
You can use Thread.Sleep() with locking
private object locking = new object();
lock (locking )
{
Thread.Sleep(500);
//Your code to run here
}
https://msdn.microsoft.com/en-us/library/system.threading.thread.sleep(v=vs.110).aspx
Just writen super simple class with System.Threading.Thread; With a little different approach Usage.
var delayedCaller = new DelayedTimeout(() => HelloWorld(123), 500, false);
delayedCaller.ResetTimer();
delayedCaller.ResetTimer();
Currently, you can do it very simple with the following class
public class DelayedTimeout
{
readonly Timer _timer;
readonly int _timeoutMs;
public DelayedTimeout(TimerCallback callback, int timeoutMs, bool startNow)
{
_timeoutMs = timeoutMs;
// Should we start now
var currentTimeoutMs = startNow ? _timeoutMs : Timeout.Infinite;
_timer = new Timer(callback, null, currentTimeoutMs, Timeout.Infinite);
}
// Constructor overloading
public DelayedTimeout(Action callback, int timeoutMs, bool startNow) :
this(delegate (object? obj) { callback.Invoke(); }, timeoutMs, startNow)
{}
public void ResetTimer()
{
_timer.Change(Timeout.Infinite, Timeout.Infinite); // Stop the timer
_timer.Change(_timeoutMs, Timeout.Infinite); // Stop the timer
}
}
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 event handler:
private void Control_Scroll(object sender, ScrollEventArgs e)
{
UpdateAnnotations();
}
Now I wish to update annotations only if user stopped scrolling, like if since last scrolling event passed 100ms, then execute action, else discard it, as it won't matter anyway.
What would be the easiest/reusable way to do that, preferably some static method like public static void DelayedAction(Action action, TimeSpan delay).
Using .NET 4.0.
See this answer to an Rx (Reactive Extensions) question. (You can use Observable.FromEvent to create an observable from an event.)
I would go with something like this
class MyClass
{
private System.Timers.Timer _ScrollTimer;
public MyClass()
{
_ScrollTimer= new System.Timers.Timer(100);
_ScrollTimer.Elapsed += new ElapsedEventHandler(ScrollTimerElapsed);
}
private void ResetTimer()
{
_ScrollTimer.Stop();
_ScrollTimer.Start();
}
private void Control_Scroll(object sender, ScrollEventArgs e, TimeSpan delay)
{
ResetTimer();
}
private void ScrollTimerElapsed(object sender, ElapsedEventArgs e)
{
_ScrollTimer.Stop();
UpdateAnnotations();
}
}
Every time the user scrolls, the timer gets reset and only when scrolling stops for 100ms the TimerElapsed gets fired and you can update your annotations.
I tried this with several controls on the form at the same time, and it is reusable by outside.
private void vScrollBar1_Scroll(object sender, ScrollEventArgs e)
{
if (DelayedAction(100, sender))
UpdateAnnotations();
}
Dictionary<object, Timer> timers = new Dictionary<object, Timer>();
bool DelayedAction(int delay, object o)
{
if (timers.ContainsKey(o))
return false;
var timer = new Timer();
timer.Interval = delay;
timer.Tick += (s, e) =>
{
timer.Stop();
timer.Dispose();
lock(timers)
timers.Remove(o);
};
lock(timers)
timers.Add(o, timer);
timer.Start();
return true;
}
The dictionary is locked, because if a user cannot hit two controls at the same time, a timer might be inserted at the same time as another one is removed.
Try this class:
public class ActionHelper
{
private static Dictionary<Delegate, System.Threading.Timer> timers =
new Dictionary<Delegate, System.Threading.Timer>();
private static object lockObject = new object();
public static void DelayAction(Action action, TimeSpan delay)
{
lock (lockObject)
{
System.Threading.Timer timer;
if (!timers.TryGetValue(action, out timer))
{
timer = new System.Threading.Timer(EventTimerCallback, action,
System.Threading.Timeout.Infinite,
System.Threading.Timeout.Infinite);
timers.Add(action, timer);
}
timer.Change(delay, TimeSpan.FromMilliseconds(-1));
}
}
public static void EventTimerCallback(object state)
{
var action = (Action)state;
lock (lockObject)
{
var timer = timers[action];
timers.Remove(action);
timer.Dispose();
}
action();
}
}
Features:
Thead safe
Supports multiple concurrent actions
Usage:
private void Control_Scroll(object sender, ScrollEventArgs e)
{
ActionHelper.DelayAction(UpdateAnnotations, TimeSpan.FromSeconds(1));
}
Just be aware that the method is called in a separate thread. If you need to do UI work, you need to use Control.Invoke (WinForms) or Dispatcher.Invoke (WPF):
// The method is contained in a Form (winforms)
private void UpdateAnnotations()
{
if (this.InvokeRequired)
this.Invoke(new Action(UpdateAnnotations));
else
{
MessageBox.Show("Method is called");
}
}
Could you not store the time the event was fired (DateTime.Now) and when ever it's called check how long it's been since the last time (e.g. DateTime.Now - lastExecutionTime > minTime)
** Update **
Or a more generic way based on your static helper idea:
public static void DelayedAction(Action action, TimeSpan delay)
{
var delayedActionTimer = new Timer(x => action(), null, delay, TimeSpan.FromMilliseconds(-1));
}
Needs work obviously... for instance you could store the timer in a field and reset (change) the delay each time the user scrolls
I'm using following code to call Method B after N seconds method A is called. If method A
is called again within the N seconds timeout, i have to reset the time counting back to N seconds.
I cannot reference System.Windows.Form in my project, so I cannot use System.Windows.Form.Timer.
The method B must be called in the same thread A is called.
private void InitTimer()
{
timer = new BackgroundWorker();
timer.WorkerSupportsCancellation = true;
timer.WorkerReportsProgress = true;
timer.DoWork += delegate(object sender, DoWorkEventArgs e)
{
var st = DateTime.Now;
while (DateTime.Now.Subtract(st).TotalSeconds < 10)
{
if (timer.CancellationPending)
{
e.Cancel = true;
return;
}
}
};
timer.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled)
{
MethodB();
}
else
{
timer.RunWorkerAsync();
}
};
}
public void MethodA()
{
if (timer.IsBusy)
timer.CancelAsync();
else
timer.RunWorkerAsync();
}
public void MethodB()
{
//do some stuff
}
Actually the code work, but i think it's a bit confounding. Do you know if there is a best practices to achieve the same result?
It's a shame you're stuck on .NET 2.0, because Rx extensions has a Throttle method that achieves this effect quite elegantly.
Sadly Rx requires at least .NET 3.5 SP1.
Oh well! You can always use a System.Threading.Timer to get this done instead. Synchronization can be provided by leveraging the current SynchronizationContext (this is what BackgroundWorker does).
Here's a sketch of a LaggedMethodPair class to illustrate this approach. The class takes three inputs in its constructor: an Action to be performed on-demand, another Action to serve as the callback that will be invoked when a given timeout has elapsed, and, of course, the timeout itself:
public sealed class LaggedMethodPair
{
private SynchronizationContext _context;
private Timer _timer;
private Action _primaryAction;
private Action _laggedCallback;
private int _millisecondsLag;
public LaggedMethodPair(Action primaryAction,
Action laggedCallback,
int millisecondsLag)
{
if (millisecondsLag < 0)
{
throw new ArgumentOutOfRangeException("Lag cannot be negative.");
}
// Do nothing by default.
_primaryAction = primaryAction ?? new Action(() => { });
// Do nothing by default.
_laggedCallback = laggedCallback ?? new Action(() => { });
_millisecondsLag = millisecondsLag;
_timer = new Timer(state => RunTimer());
}
public void Invoke()
{
// Technically there is a race condition here.
// It could be addressed, but in practice it will
// generally not matter as long as Invoke is always
// being called from the same SynchronizationContext.
if (SynchronizationContext.Current == null)
{
SynchronizationContext.SetSynchronizationContext(
new SynchronizationContext()
);
}
_context = SynchronizationContext.Current;
ResetTimer();
_primaryAction();
}
void ResetTimer()
{
_timer.Change(_millisecondsLag, Timeout.Infinite);
}
void RunTimer()
{
_context.Post(state => _laggedCallback(), null);
}
}
I wrote a sample Windows Forms app to show this class in action. The form contains a LaggedMethodPair member with a timeout of 2000 ms. Its primaryAction adds an item to a list view. Its laggedCallback adds a highlighted item to the list view.
You can see that the code runs as expected.
I would encapsulate this functionality into a timer class with events that other classes can subscribe to (for example a timer.tick event).
I am trying to use AutoResetEvent, because it is capable to wait for a signal. I use it to have worker waited for the signal from A(), and if it has been too long B() will be called.
class Caller
{
AutoResetEvent ev = new AutoResetEvent(false);
public void A()
{
ev.Set();
// do your stuff
Console.Out.WriteLine("A---");
}
void B()
{
Console.Out.WriteLine("B---");
}
public void Start()
{
var checker = new BackgroundWorker();
checker.DoWork += new DoWorkEventHandler(checker_DoWork);
checker.RunWorkerAsync();
}
void checker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (!worker.CancellationPending)
{
bool called = ev.WaitOne(TimeSpan.FromSeconds(3));
if (!called) B();
}
}
}
I have tested my class roughly and it is working fine so far. Note that B will be called from worker thread, so you have to do the synchronization in B() if needed.