1

I want to execute a method takes inputs every specific time (such as 25 ms) under the following conditions:

  1. run the method every 25 ms.
  2. If the method delay was greater than 25 ms, stop execution and start from the beginning for the new inputs.

Hint: I used the following code but it doesn't stopped the execution if the method delay > 25 ms

private Timer timer1; 
public void InitTimer()
{
   timer1 = new Timer();
   timer1.Tick += new EventHandler(timer1_Tick);
   timer1.Interval = 1000/40; 
   timer1.Start();
}

private void timer1_Tick(object sender, EventArgs e)
{
   mymethod()
}    

Thanks

abdelhamedia
  • 64
  • 1
  • 10
  • The method itself should have a capability of being suspended/terminated in the middle of its execution. – Tigran Jul 07 '15 at 14:34
  • 2
    1000/25 != 25ms, its 40ms. Hopefully you realize that Windows and .NET are not hard real-time systems and your 25 millisecond routine is run "somewhere" around 25ms, but can be much, much longer if something else runs and blocks it. – Ron Beyer Jul 07 '15 at 14:37
  • @Tigran, Please clear for me more, it's a very simple method, take some inputs and do certain operations according to the inputs every 25 ms. the problem here some times the inputs leads to delay method more than 25 ms, at this point I want to stop method execution and start processed new inputs. – abdelhamedia Jul 07 '15 at 14:40
  • @RonBeyer, Thanks, sorry I changed the code above. – abdelhamedia Jul 07 '15 at 14:42
  • @Abdo you should stop timer before your method call. and start again when it has been completed. i give code with my answer. – Nuri YILMAZ Jul 07 '15 at 14:43
  • So you want to stop the current method and start again if it takes longer than 25ms to complete? To do that, you'll want to use a `Task` with a `CancellationToken` and when the timer runs, check if the `Task` is still running, if it is, cancel it and start a new one. – Ron Beyer Jul 07 '15 at 14:49

3 Answers3

2

I didn't get a chance to run this and check its 100% accurate, but this should give you the idea about how to use a Task to solve the problem:

public class Something
{
    public Task _myMethodTask;
    public CancellationTokenSource _cancelToken;
    public Timer _myTimer;
    public Random _rnd;

    public void Start()
    {
        _rnd = new Random((int)DateTime.Now.Ticks);
        _myTimer = new Timer(TimerElapsedHandler);
        _myTimer.Change(25, 25);
    }

    public void TimerElapsedHandler(object state)
    {
        if (!_myMethodTask.IsCompleted)
        {                
            //The current task is taking too long
            _cancelToken.Cancel();
        }

        _cancelToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(25));
        _myMethodTask = new Task(() => MyMethod(), _cancelToken.Token);
    }

    public void MyMethod()
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        int delayTimeMs = _rnd.Next(5, 50);

        while (sw.ElapsedMilliseconds < delayTimeMs)
        {
            try
            {
                _cancelToken.Token.ThrowIfCancellationRequested();
                Thread.Sleep(1);
            }
            catch (TaskCanceledException)
            {
                return;
            }

        }
    }
}

What is happening is that it uses a timer (in this case a System.Threading.Timer) that runs every 25 milliseconds. It starts a new task with a cancellation time of 25 milliseconds. The task is the MyMethod(), and just to simulate a long running process it delays a random time. You can add some Console.WriteLine or Debug.WriteLine calls to see it working.

The important thing to note is that you have to call _cancelToken.Token.ThrowIfCancellationRequested(); periodically, not just once. The method throws an exception if the cancellation was requested and you can clean up before exiting if need be. If you don't call it, the task will not cancel.

Ron Beyer
  • 11,003
  • 1
  • 19
  • 37
  • Can someone show me a version of the solution that is using Rx? For this kind of tasks I would use that (Reactive Extension). – ntohl Jul 08 '15 at 07:49
0

Simply use System.Timers.Timer, do not use System.Threading.Timer

timer1 = new System.Timers.Timer(1000 / 25);

and modify Tick event

private void timer1_Tick(object sender, EventArgs e)
{
   timer1.Stop();
   mymethod()
   timer1.Start();
}

but there is no waranty mymethod throw exception etc. you should manage them also.

Nuri YILMAZ
  • 4,291
  • 5
  • 37
  • 43
0

I have this solution using reactive extension. I had to increase the timespans by an order, because the sleep is not precise enough. The following code shows that the actual variable is increasing even when the previous failed.
You have to tweak some on the mymethod() function also. If the expensive method is an I/O heavy function, than split it up into buffers, and each buffers writing/reading You can check if the token is still not cancelled. This way, the length of the blocking part can be adjusted.

static Random rand = new Random();

static void Main(string[] args) {
    var obs = Observable.Interval(TimeSpan.FromMilliseconds(250)).Do<long>(i =>
    {
        CancellationTokenSource source = new CancellationTokenSource(250);
        ReadNext(source.Token, i);
    }).Publish();
    var disp = obs.Connect();
    Console.ReadKey();
    disp.Dispose();
    Console.ReadKey();
}

static private void ReadNext(CancellationToken token, long actual) {
    int i = rand.Next(4);
    for(int j = 0; j < i; j++) {
        Thread.Sleep(100);
        if(token.IsCancellationRequested) {
            Console.WriteLine(string.Format("method cancelled. cycles: {0}, should be 3. Now should be last (2): {1}", i, j));
            return;
        }
    }
    Console.WriteLine(string.Format("method done in {0} cycles. Preserved index: {1}.", i, actual));
}

example printout:

method done in 2 cycles. Preserved index: 4.
method done in 0 cycles. Preserved index: 5.
method done in 0 cycles. Preserved index: 6.
method done in 2 cycles. Preserved index: 7.
method done in 1 cycles. Preserved index: 8.
method cancelled. cycles: 3, should be 3. Now should be last (2): 2
method done in 0 cycles. Preserved index: 10.
method cancelled. cycles: 3, should be 3. Now should be last (2): 2
method cancelled. cycles: 3, should be 3. Now should be last (2): 2
method cancelled. cycles: 3, should be 3. Now should be last (2): 2
method done in 1 cycles. Preserved index: 14.
method cancelled. cycles: 3, should be 3. Now should be last (2): 2
method done in 1 cycles. Preserved index: 16.
method done in 2 cycles. Preserved index: 17.
method cancelled. cycles: 3, should be 3. Now should be last (2): 2
method done in 2 cycles. Preserved index: 19.
method done in 1 cycles. Preserved index: 20.
method done in 2 cycles. Preserved index: 21.
method done in 2 cycles. Preserved index: 22.
method done in 1 cycles. Preserved index: 23.
method done in 1 cycles. Preserved index: 24.
method done in 1 cycles. Preserved index: 25.
method done in 2 cycles. Preserved index: 26.
method done in 1 cycles. Preserved index: 27.
ntohl
  • 2,067
  • 1
  • 28
  • 32