I need to set up a simple elapsed Timer in C# (MonoBehavior) that calls a method when complete, but can also be cancelled before finishing. If cancelled or stopped, it automatically resets its interval. I don't need anything fancy like threading.
Reading over the documentation on C# Timers https://msdn.microsoft.com/en-us/library/System.Timers.Timer%28v=vs.110%29.aspxstill a bit confused. For instance, when you set mytimer.Enabled=false does it also reset the timer back to 0? Perhaps I should be looking at Coroutines instead?(this is for Unity)
In AS3 I would do something like this
private var _delayTimer:Timer;
//create a Timer object that runs once for 1 second
_delayTimer = new Timer(1000,1);
//add handler
_delayTimer.addEventListener(TimerEvent.COMPLETE, onDelay);
//start timer
_delayTimer.start();
private function onDelay(e:TimerEvent):void{
trace('delay finished!);
}
//some other method to interrupt-stop and reset the delay timer
private function foo():void{
_delayTimer.reset();
}
By using System.Timers.Timer, you are using multi-threading - it's quite likely this is not what you want.
Instead, you probably want System.Windows.Forms.Timer - this will post the timer event back on the UI thread (if you're using Windows Forms, of course).
You can use Start and Stop the way you want, because there's actually no ticking clock - it just registers a callback from Windows in the future, basically.
Relevant piece of documentation from MSDN:
Calling Start after you have disabled a Timer by calling Stop will cause the Timer to restart the interrupted interval. If your Timer is set for a 5000-millisecond interval, and you call Stop at around 3000 milliseconds, calling Start will cause the Timer to wait 5000 milliseconds before raising the Tick event.
Related
I have the following scenarios using System.Timers.Timer.
Create a Timer object and assign the method
_JobListener.Enabled = false;
_JobListener.Elapsed += (this.JobListener_Elapsed);
Within JobListener_Elapsed, I created another thread to run some functions.
JobListener_Elapsed
{
//stop the timer
_JobListener.Enabled = false;
System.Threading.Thread pollThread = new Thread(JobListener_ElapsedAsync);
pollThread.Start();
//join to the main timer thread
pollThread.Join();
//restart the timer
_JobListener.Enabled = true;
}
Within JobListener_ElapsedAsync, I log the timer Enabled status.
private void JobListener_ElapsedAsync()
{
try{
log Timer.Enabled
some other code
}finally
{
_JobListener.Enabled = true;
}
}
However, I can see some times, it can see the timer status to be true, which is wrong. The timer should be stopped when JobListener_ElapsedAsync is running.
Any idea?
There are two main classes of timer in the .NET Framework: thread timers, and window timers.
System.Threading.Timer is the basic class for thread timers. This wraps the Windows waitable timer object. The Tick event is fired on the ThreadPool. It does not check whether all handlers for a previous Tick have returned before firing Tick again. The timer should fire on time - it is not delayed.
System.Windows.Forms.Timer wraps the Windows SetTimer API. The Tick event is fired on the thread that creates the timer. If that isn't a UI thread it won't actually fire. Timers are the lowest priority messages; they are only generated by GetMessage/PeekMessage when no other messages are outstanding. Therefore they can be delayed significantly from when they should be generated.
System.Timers.Timer wraps System.Threading.Timer. If you have set its SynchronizingObject property to something, when the underlying timer fires, it will use ISynchronizeInvoke.Invoke to get onto that object's thread (analogous to Control.Invoke - indeed Control implements ISynchronizeInvoke). It blocks the thread pool thread in the process. If SynchronizingObject is null it just fires the Elapsed event on the thread pool thread. The only real use for this class is if you need timers for UI components to be fired on time. If you don't need to synchronize, use a System.Threading.Timer instead.
If you need to ensure that a previous Tick event is fully handled (all handlers have returned) before the next one is started, you need to either:
Make the timer one-shot rather than periodic, and have the last handler set up another shot when it finishes executing
Use a lock or Monitor to prevent two threads entering the handler (but you could use up all threads in the thread pool)
Use Monitor.TryEnter to only enter the handler if the previous one has finished (but you could miss some Ticks).
I'm developing a Chip-8 emulator in C#, I have completed partially almost every aspect but there is something I'm still wondering about speed control of the emulator.
What I'm doing now is assuming that I get 60 frames per second, I use a timer that fires 1/60 seconds in the following manner (pseudocode):
timer_ticked()
{
for(int i = 0; i < Settings.CyclesPerFrame; i++)
{
EmulateCycle();
}
if (IsDrawFlagSet)
{
DrawGraphics();
}
}
I'm using a high resolution timer called microtimer http://www.codeproject.com/Articles/98346/Microsecond-and-Millisecond-NET-Timer , I believe that the timer doesn't wait for timer_ticked to finish to fire the next cycle (fon instance, creating a new thread) and I'm having an issue with threads and the form since trying to paint to a window with GDI (using the control.GetGraphics() method) seems to be threadsafe, but trying to create an SDLDotNet approach (just an example) doesnt.
Which approach do you think is best to control the speed of an emulator without getting into timer thread insanity?
PS: You can find the source code of the emulator in GitHub: https://github.com/AlFranco/C8POC
Thank you!
If you tick method is being called again before it's done, the problem isn't with your timer. It's because the processing is taking longer than that 16.6 ms. Getting a better timer isn't going to solve your problem.
That said, you can prevent reentrancy in a couple of ways.
You could just disable the timer when you enter the callback, and re-enable it when you're done. That'll prevent multiple invocations:
timer_ticked()
{
timer.Enabled = false;
// do stuff here
timer.Enabled = true;
}
Note that this doesn't give you a perfect 16.6 ms tick frequency. Rather, the next tick will occur 16.6 ms (approximately) after you enable the timer. Your real period is 16.6 ms plus however long it takes to do the processing.
The only time this would fail is if the timer_ticked method wasn't called before the next tick happened.
If you want to guarantee that you can't get concurrent ticks, you can use a System.Threading.Timer and set it up as a one-shot (no periodic signaling). For example:
Timer myTimer = new Timer(timer_tick, null, 16, -1);
The -1 in the last parameter tells it not to be a periodic timer. It'll fire once and stop.
Then, in your timer tick:
timer_tick()
{
// do stuff
// restart the timer
myTimer.Change(16, -1);
}
Edit
You can't easily tell the timer not to issue a tick if the handler is still processing the previous tick. You can, however, prevent the timer tick handler from doing anything on that subsequent tick. You simply use a Monitor:
private object timerLock = new object();
timer_ticked()
{
if (!Monitor.TryEnter(timerLock))
return;
try
{
// do stuff here
}
finally
{
Monitor.Exit(timerLock);
}
}
The problem with such a solution is that if your timer is set of 16 ms and the handler takes 17 ms, then your effective update rate will be once every 32 ms, because the second tick is essentially ignored. You're better off with the one-shot timer deal.
Another possibility is to use a Stopwatch to time how long your handler takes, and subtract that from the next delay period:
timer_ticked()
{
var sw = Stopwatch.StartNew();
// do stuff
timer.Change(16-sw.ElapsedMilliseconds, -1);
}
But it's not quite that simple. If it takes longer than 16 ms for the handler to do its thing, you'll end up with a negative delay. So you'll want:
var delayTime = Math.Max(0, 16 - sw.ElapsedMilliseconds);
timer.Change(delayTime, -1);
But again, if your handler regularly takes longer than the timer delay, then this isn't going to help you. You'll either have to reduce your timer frequency (i.e. make a longer delay) or optimize your processing code.
I have a console app. I need to implement a do while that loop infinitely and a thread that at every 3 seconds returns a list of items from a page. How can I do that? I have a methold called getId( string URL) . how do I implement the thread in the do while?
Using System.Timers.Timer class:
string url = "www";
System.Timers.Timer timer = new System.Timers.Timer(3000);
timer.Elapsed += (o, e) => this.GetId(url);
timer.Start();
Timer is designed for use with worker threads in a
multithreaded environment. Server timers can move among threads to
handle the raised Elapsed event, resulting in more accuracy than
Windows timers in raising the event on time.
The Timer component raises the Elapsed event, based on the value of
the Interval property
I would not use a timer - what happens if the item retrieval takes longer than three seconds?
Can you live with a sleep(3000) loop?
Rgds,
Martin
I have a while loop and in this loop, there is a if condition.
But condition will be changed by a timer. But timer never change global variable.
I cant understand.
Where is the problem?
Example:
bool enterHere = false;
Timer timer = new Timer(); //Timer Started
private void timer_Tick(object Sender, ...)
{
enterHere = true;
}
private void function()
{
while(...)
{
if(enterHere)
{
//Never enter here
}
}
}
As another lesson in why you should always post your real code when asking questions on SO...
It appears the solution to your problem is quite a bit simpler than the other answers suggest. The timer's Tick event is never going to be raised, thus the value of the enterHere variable is never going to be changed, because you never actually start the timer. More specifically, this line is incorrect:
Timer timer = new Timer(); //Timer Started
The constructor does not start the timer; you need to call its Start method. This is confirmed by the documentation, which says:
When a new timer is created, it is disabled; that is, Enabled is set to false. To enable the timer, call the Start method or set Enabled to true.
Absolutely no reason to muck about with things like Application.DoEvents if you don't have to.
I assume you're using a System.Windows.Forms.Timer in which case the Tick event will run on the same thread as your function(). You can put
Application.DoEvents();
Inside your loop to get the timer to tick. Alternatively you could use an other timer (such as the System.Threading one), which executes on a different thread.
What else are you doing in the WHILE(...) loop and have you checked the processor usage when your code is running? If the loop is running very quickly there is no time for your app to process it's messages and react to the timer message.
As deltreme says, inserting Application.DoEvents(); in the loop should give it a chance to process the message.
Ideally the timer should be running in a different thread if you have a loop like that. The timer will never be able to raise the event.
Alteratively you could call DoEvents() to allow the timer to do it's work
Whilst debugging my program in VS 2008 I have come across the following error:
The CLR has been unable to transition from COM context 0x34fc1a0 to COM context 0x34fc258 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this
It appears to be deadlocking even though the code only contains a simple C# timer: See Snippet Below:
private void RequestWork()
{
// The timer will be re-intialised if there are still no wating jobs in the database
StopTimer();
// assign all the threads some work
InitialiseTimer();
}
/// <summary>
/// Initialise a timer with a timer interval configured from app.config. Enable the timer and
/// register an appropriate event handler
/// </summary>
private void InitialiseTimer()
{
if (m_Timer == null)
{
// look up the default backoff time from the config
string backOffInt = ConfigurationSettings.AppSettings["BackOffInterval"];
int backoffInterval = 1000;
m_Timer = new System.Timers.Timer();
// set the timer interval to 5 seconds
m_Timer.Interval = backoffInterval;
m_Timer.Elapsed += new ElapsedEventHandler(m_Timer_Elapsed);
}
m_Timer.Enabled = true;
}
private void StopTimer()
{
if (m_Timer != null)
{
m_Timer.Enabled = false;
}
}
void m_Timer_Elapsed(object p_Sender, ElapsedEventArgs p_E)
{
RequestWork();
}
As far as I know the timer should run, elapse and then initialise again, I can see no local reason for a deadlock.
I am aware of how to turn this error msg off but feel that this is not a solution, instead it is masking the problem.
You can turn this off if you think you've definitely not got a deadlock situation:
Debug->Exceptions->Managed Debug Assistants menu in Visual Studio and uncheck the ContextSwitchDeadlock
This is an infinite loop. You need to let your application pump some messages at least once every 60 seconds to prevent this exception to happen.
Try calling System.Threading.Thread.CurrentThread.Join(10) once in a while. There are other calls you can do that let the messages pump.
It seems that you are adding a new event handler each time you call InitialiseTimer. That way m_Timer_Elapsed will be called as many times as it has been added.
You should add the event handler just one time.
If your application hangs or not reponse even after you uncheck the box against contextswitchdeadlock. Put the following line before call of method or for loop.
In C#
System.Windows.Forms.Application.DoEvents();
and VB.NET / VB / ASP.NET
DoEvents()
Couple thoughts/questions:
1) The code snippet looks like your interval is every 1 second (not 5 as mentioned in the comments).
2) The big question is what is RequestWork() doing?
Without knowing what RequestWork() is doing, we can't really comment on why you are seeing a ContextSwitchDeadlock.
Somethings to think about with respect to this method
a) how long does it take?
b) is it accessing GUI elements?
Some MSDN comments on Elapsed:
If you use the Timer with a user
interface element, such as a form or
control, assign the form or control
that contains the Timer to the
SynchronizingObject property, so that
the event is marshaled to the user
interface thread.
-and-
The Elapsed event is raised on a
ThreadPool thread. If processing of
the Elapsed event lasts longer than
Interval, the event might be raised
again on another ThreadPool thread.
Thus, the event handler should be
reentrant.
I'm thinking since you have a 1 second timer, you might want to look into what happens in RequestWork and see how long its taking.