4

I have the following piece of code, that runs a "select" on certain table that needs to be monitored every 200 miliseconds

timerMonitoreoOrdenes = new System.Timers.Timer(FRECUENCIA_MONITOREO_ORDENES);
timerMonitoreoOrdenes.Elapsed += new ElapsedEventHandler(timerMonitoreoOrdenes_Elapsed);
timerMonitoreoOrdenes.Enabled = true;
timerMonitoreoOrdenes.AutoReset = true;

In the timerMonitoreoOrdenes_Elapsed method I run a stored procedure that returns a DataSet and for each row I am creating a new Object that is stored in memory Queue

The program is designed to be running all the time (like a windows service) but after the programs runs for a few hours I am getting this exception

   System.OutOfMemoryException: 
   in System.Threading.ExecutionContext.CreateCopy()
   in System.Threading._TimerCallback.PerformTimerCallback(Object state)

The reason that I am doing this like this is becase there is an external program that is inserting records on the DB with status=0 and I need to take those records, process them and set the status=1. There are some Thread that are taking records from the Queue

Is important to mention that This is for a REAL-TIME-TRADING application that 1 second delay in the information is too high

  • I want to know if the System.OutOfMemoryException is being thrown because of the timer autoreset ?
  • Should I create a Thread or use Thread.Sleep instead of a Timer to check for certain records that were inserted by another process ?
Mauricio Gracia Gutierrez
  • 10,288
  • 6
  • 68
  • 99
  • Well, you have described how you are putting data from your DataSet into a Queue but at no time have you suggested you ever take anything out of your queue. The other thought that occurs is that if the SQL query takes longer than 200ms on average then you will be running into a lot of trouble. More detail needed on what you are doing before an answer can be given. And also possibly why you are doing this since there may be a better solution to your original problem than regular selects on a DB table. – Chris Jan 03 '14 at 13:22
  • @Chris I have added more information to the question. If more details are needed I can provide them – Mauricio Gracia Gutierrez Jan 03 '14 at 13:27
  • Off the top of my head I don't remember if System.Timers.Timer itself prevents the callback from being re-entered. Could it be that your callback gets called again before the previous call completes? What happens if you disable the timer from within the callback so that this can't happen? – 500 - Internal Server Error Jan 03 '14 at 13:33
  • @500-InternalServerError: It will happily have multiple worker threads at the same time which is what I was obliquely referring to in my comment. Best way to deal with this I'd have thought was to not have it auto-reset and start the timer again at the end of the work it does. – Chris Jan 03 '14 at 13:35
  • @Chris that sounds much better I will try that and let you know – Mauricio Gracia Gutierrez Jan 03 '14 at 13:36
  • Also I'd think hard about whether you really need it to be as responsive as every 200ms. I would imagine that for most uses running it every 5 seconds (or more) would be sufficient. – Chris Jan 03 '14 at 13:38
  • This is for a REAL-TIME-TRADING application that 1 second delay in the information is too high – Mauricio Gracia Gutierrez Jan 03 '14 at 14:03

3 Answers3

7

Sure, that is quite possible. A Timer that's ticking with AutoReset = true is a ticking time bomb. Things go drastically wrong if the Interval is too short. Using 200 msec is pretty risky, dbase update queries can easily take longer than that. Particularly so if the column you are looking for isn't indexed.

Your Elapsed event handler will run again even though the previous one isn't completed. On another thread-pool thread. Each thread will consume a megabyte of memory, plus whatever you need for the query and processing. This just continues, creating ever more threads. The thread-pool manager will make an effort to limit this but the maximum number of threads is allows to run is very high. High enough to cause arbitrary code to eventually fall over with OOM.

Use AutoReset = false and call Start() again at the end of your Elapsed event handler. And use a reasonable Interval that's at least close to the actual processing time. And add an index on that column so the dbase engine doesn't have to look at every record in the table.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • The interval is SET in the code and is mentioned in the question as 200 ms – Mauricio Gracia Gutierrez Jan 03 '14 at 13:42
  • Okay, still very short. There's just no reason whatsoever to take the risk, it will *never* work better if these update queries overlap. Rather the reverse, you're liable to process the same record more than once. Use a memory profiler if it still falls over. – Hans Passant Jan 03 '14 at 13:44
  • How else can I take records from a DB table as fast as posible. I have a producer that is inserting them on the table as fast as 700 records por minute – Mauricio Gracia Gutierrez Jan 03 '14 at 13:46
  • 3
    If your code takes longer than the rate at which the producer inserts them then you can *never* win. It should never matter how quickly you update these records after the producer inserts them, you cannot possibly provide a guarantee, only that you can keep up. You'll at least need to add an index on that column so the query won't be so expensive. – Hans Passant Jan 03 '14 at 13:50
  • 1
    I assume you can't change the producer to deliver them more effectively to your consumer to try to reduce overhead a bit? That seems to be a very high volume of items. And as Hans says picking them up more often won't make any difference in the long run to how long processing them takes. – Chris Jan 03 '14 at 14:02
  • 1
    @MauricioGracia - can your DB not provide a trigger and notification of table insertions? – Martin James Jan 03 '14 at 14:04
  • @MartinJames that is another approach that I will try also – Mauricio Gracia Gutierrez Jan 03 '14 at 14:05
0

Hi there i did this once, i had to split the code and make a windows service, i recommend you to do the same, use the windows service to check the hour every 10 minutes, every minute or something like that, and run the procedures.

If you are working with windows forms, the main system will be crashed a lot of times, i hope i help you with this advices.

ricardorios
  • 356
  • 1
  • 7
  • 26
  • 1
    Will this intrinsically reduce the amount of memory used for some reason? He is already running checks at regular intervals except the OP is doing 200ms instead of minutes or more. – Chris Jan 03 '14 at 13:37
  • @Chris yes it will, because a windows service is running on backgroud, and it doesn't matter the ammounts of time he will use – ricardorios Jan 03 '14 at 14:35
  • The point is that if the code being run is the same then the same amount of objects will be created and the same amount of memory will be allocated. Making it a windows service might change some stuff but not how much memory is being used because its doing the exact same thing. – Chris Jan 04 '14 at 19:39
0

Are you sure you dispose the timer correctly, by calling the Dispose() method when the timer is no longer needed? If you don't, it will create a memory leak.

tmakij
  • 26
  • 4
  • 1
    If the timer needs to be triggered as long as the program is running when and how am I suppose to call Dispose ? – Mauricio Gracia Gutierrez Jan 03 '14 at 13:37
  • Though you are right that the Timer needs to stay alive the same could apply to any objects that you are creating in your work thread. Could you be leaving around references to any objects created anywhere or not disposing of other objects correctly? – Chris Jan 03 '14 at 13:39
  • If the OutOfMemoryExcetption was happening at my code the StackTrace would have shown that – Mauricio Gracia Gutierrez Jan 03 '14 at 13:43