Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# language equivalent to ScheduledExecutorService

I'm working with a RF reader device which has C# API.Based on its API, you'll need to manually invoke its read function to read/badge a card.

So my workaround is to use Timer to execute reading every 'n' seconds.

My problem is that Timer continuously executing, regardless of Thread.sleep() was invoke inside it's action.

Timer timer = new Timer(TimerCallback, null, 500, 1000); // From main() method

// The action that Timer executes
private void TimerCallback(Object o)
{
     scan(); // Action for reading/badging card
     scand.WaitOne(); // AutoResetEvent(true)
     GC.Collect(); // Force garbage collection
}

Thread.sleep() invoke inside the scan().

In Java, I use synchronized() to wait the other thread to call invoke(). I've searched for the whole day and I can't see a workaround that will be equivalent to ScheduledExecutorService and synchronized().

I hope there is a workaround with this cause I need it as soon as possible.

Thanks!

like image 968
Erieze Lagera Avatar asked Nov 01 '22 17:11

Erieze Lagera


1 Answers

The most reliable way I could find was by re-starting the timer in the callback. This way the callback is not interrupted when active.

Timer timer = new Timer(TimerCallback, null, 500, 0); 

private void TimerCallback(Object o)
{
     scan(); 
     scand.WaitOne(); 
     timer.Change(500, 0);
}

The timer.Change re-schedules the timer.

Note: I removed the repetition in the Timer start up.

BTW: I removed the GC.Collect() as I consider that bad practice and useless in most cases.

Additionally you could get the time at the start of the method (Use Stopwatch) and calculate the required time delta to be passed to timer.Change :

Timer timer = new Timer(TimerCallback, null, 500, 0);
Stopwatch stopwatch = Stopwatch.StartNew();

private void TimerCallback(Object o)
{
     var entered = stopwatch.ElapsedMilliseconds;
     scan(); 
     scand.WaitOne(); 
     var duration = stopwatch.ElapsedMilliseconds - entered;
     var delay = Math.Max(0, 500 - duration);
     timer.Change(delay, 0);
}

This way the callback will be invoked after 500 ms minus the time it took to execute the scan functions. Set up like this and you might remove the Sleep from the Scan.

The reason for the double callback in your code is probably that the timer executes the callback on another thread when the first thread is still executing the callback.

Another solution might be to not use a Timer at all. Just loop and use a Stopwatch to calculate a time period to Sleep:

private void Scan()
{
     while(scanning)
     {
         var entered = stopwatch.ElapsedMilliseconds;
         scan(); 
         scand.WaitOne();
         var duration = stopwatch.ElapsedMilliseconds - entered;
         var delay = Math.Max(0, 500 - duration);
         Thread.Sleep(delay);
     }
}

Make sure you call this method on a separate thread (you could use a Task)

like image 129
Emond Avatar answered Nov 12 '22 22:11

Emond