56

Suppose we have a timer which runs every 10 minutes. What if the cycle of its processing takes more than 10 minutes. Does a new thread starts for that? Will it interrupt its current operation? What if a single object is mutated inside the timer?

Sorry if I do not mention any code for that because the problem is clear and also I want to know the complete answer from the viewpoint of a multi-threaded programming geek rather than finding a loose answer by trying to test it via a sample application. Actually, I want to know the logic behind its working mechanism.

Ben
  • 51,770
  • 36
  • 127
  • 149
Farshid
  • 5,134
  • 9
  • 59
  • 87

3 Answers3

58

If you're using System.Threading.Timer or System.Timers.Timer, the timer will tick again, starting a new thread. See https://stackoverflow.com/a/10442117/56778 for a way to avoid that problem.

If you're using System.Windows.Forms.Timer, then a new tick won't occur until the previous one is finished processing.

Community
  • 1
  • 1
Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • 1
    That's right. I personally do not even use .NET's timers, but implement my own; Which use threading the way that I need for the specific application, and defines the logic of new ticks in a more relevant way. If you have a problem with .NET's timers' behavior, I suggest you do the same. – daniel.gindi May 06 '12 at 14:39
  • Thank you very much. Your answer is enlightening. – Farshid May 06 '12 at 14:48
  • 3
    @daniel.gindi: Interesting. I've never had a problem with the .NET timers unless I was trying to do something that required better than 15 ms resolution. My experience has been that if your processing can't keep up with the timer frequency, then you need to slow your timer or optimize your timer callback. But to implement my own timers? Seems like a lot of work for little gain. – Jim Mischel May 06 '12 at 19:59
  • @JimMischel I don't think you want to rely on timing how much processing you do and how long it takes. You'll be much safer with doing it the right way. You can't allow possible race conditions, you can never know what will happen. - And you'll be surprised at how EASY it is to implement your own timer :-) – daniel.gindi May 08 '12 at 06:32
  • 1
    I think it's important to note that even with a Windows Forms timer, there are still potential sources of re-entrancy. In particular, anything which pumps the message queue (an `Await` or something explicit on the dispatcher seems most likely) will result in re-entrant code on the UI thread. – Craig Nov 18 '22 at 17:01
3

Put your code in a Monitor.TryEnter()

object timeCheck= new object();

void Timer()
{
    Monitor.TryEnter(timeCheck) 
    {
        //Code that might take too long 
        //...
        Monitor.Exit();
    }
}
DaveShaw
  • 52,123
  • 16
  • 112
  • 141
rare
  • 31
  • 1
  • 2
    I second that. Have to watch out for lock/Monitor.TryEnter with no timeout though, as this will just queue loads of threads potentially. TryEnter with a timeout no larger than your interval is the absoloute minimum here. – M Afifi May 08 '12 at 12:27
  • 1
    I upvoted @MAfifi's comment, before realizing he is wrong. [`TryEnter` *with no time interval parameter*](https://msdn.microsoft.com/en-us/library/4tssbxcw(v=vs.110).aspx), or with time interval of zero, **never blocks** (See "Remarks" in linked doc). – ToolmakerSteve May 24 '17 at 09:38
1

to prevent reentrancy, you might use a static boolean that tells wether the function is allready beeing executed. User a try/Catch/finally and set this boolean to false in the finally to ensure that the boolean does not remain false if you made a mistake in code or if the code failed.
For a faster timer, reentrancy should be prevented by using semaphore (mutex).

GameAlchemist
  • 18,995
  • 7
  • 36
  • 59
  • A semaphore might work, but a simple boolean flag might cause trouble. Do not forget that the timer starts a new Thread for each execution..... – daniel.gindi May 06 '12 at 15:07
  • a simple boolean, yes is not enough for a fast timer, but for long (>200 ms) timer, the risk of dual reentrancy seems pretty null. – GameAlchemist May 06 '12 at 15:24
  • 2
    Yeah but you know, when there's a rare chance that something like that will happen, you can be sure that it WILL happen... The "Rule of uncertainty" of computer science :-) – daniel.gindi May 06 '12 at 15:27
  • :-) if you put the logic that manages the bolean ( if (FunctionExecuting) then return; FunctionExecuting=True;) at the begining of the function, Odds that *TWO* reentrant call will occur *at the same time* for such a short code with a >200ms are... i don't know how to say how low... Aren't you rather talking about murphy's law ? :-) – GameAlchemist May 06 '12 at 15:39
  • .. or just dump the timer/s and use a sleep() loop and a bit of arithmetic. – Martin James May 06 '12 at 15:41
  • Well you know how it is, you plan on something that you know cannot happen because the odds are so low... Then on real time it crashes, and you cruse Murphy. So yes, I guess, it's murphy's law :-) – daniel.gindi May 06 '12 at 15:41
  • 1
    At first, @MartinJames comment puzzled me [There are many situations where `Sleep` should be *avoided*, not recommended], but after carefully reading [his answer elsewhere](https://stackoverflow.com/a/10441554/199364), I do recommend it (though see my recommended change in a comment there). – ToolmakerSteve May 24 '17 at 09:24
  • @daniel.gindi - I don't see how that logic could get reentered too quickly, given that the next execution won't happen until the timer fires again, and the "protective" test is at the start of the thread. To be a problem, the timer would have to fire again *before* the second line of code runs. – ToolmakerSteve May 24 '17 at 09:33
  • @ToolmakerSteve If you have a boolean being checked at the beginning of the thread code, the boolean is also being reset at the end of the thread. Which means the *thread* is in control of the synchronization, and is not doing any actual synchronization, which is not safe. A Mutex here is the "working" solution. But as you mentioned - the actual *better* solution - is Martin James' answer. – daniel.gindi May 24 '17 at 10:04