2

I am having issues with all the possible current and outdated ways to implement a method that is continuously running in the background to obtain data which is then displayed by the UI.

Generally speaking I stumbled upon async/await usage in this answer while also reading about various timers in this answer.

My scenario is the following: I want to read data every 250ms and update my UI based on the data obtained. A naive approach would be a very simple call like this:

while (true) {
    // do stuff
    Thread.Sleep(250);
}

This code would not be called every 250ms depending on how long the execution in the body actually took! Furthermore in contrast to the timer specific answer I do not want to wait until this method completes to call it again, hence if the method turns out to take too long for one tick I still want to call it every 250ms as exactly as possible and abort the previous tick execution if necessary.

Should I use a generic Timer, a DispatcherTimer, a Task etc? Do you have further references and related questions which are up to date?

Community
  • 1
  • 1
Christian Ivicevic
  • 10,071
  • 7
  • 39
  • 74
  • In an `async` method, you could try `await Task.Delay(250)`. – devRicher Jan 08 '17 at 12:41
  • So, if the method takes longer than 250ms, you want to abort and start a new run. But if many calls take longer than 250ms you'll end up with no completions. I don't see how you want to try to get a result in as close as 250ms in such cases. – Peter Bons Jan 08 '17 at 12:55
  • The method is not designed to return an allover result at the end but smaller results during the execution. Furthermore in my particular use case the method will take longer than 250ms in very exceptional situations when the system has some kind of lag/hickup. I am acquiring the data in a order where I can say that the results early on in the call are more important than the ones at the end hence I want to have the first chunks updated more often in situation where the method does not finish. - Do you have any idea on completely different design approaches to this? – Christian Ivicevic Jan 08 '17 at 13:01

1 Answers1

1

So if I understand it correctly you want to run some code at every 250ms. Even if the code takes 40ms, waiting for 250ms using a Thread.Sleep(250) is not desired because in total it will take 290ms.

You could use a Timer or use something like this:

const int MaxAllowTimeInMs = 250;

async Task Main()
{
    while(true)
    {
        var cts = new CancellationTokenSource();
        cts.CancelAfter(MaxAllowTimeInMs);

        try
        {           
            await Task.WhenAll(new[] 
            { 
                MyTask(cts.Token), 
                Task.Delay(MaxAllowTimeInMs) 
            });
        }
        catch (OperationCanceledException)
        {
            // MyTask took longer than 250ms 
        }
    }
}

async Task MyTask(CancellationToken ct)
{
    // Get some data
    ...

    ct.ThrowIfCancellationRequested(); 

    // If you reach this point, you can process stuff knowing that it took less than 250ms to get it.
}

This code allows MyTask to complete in 250ms. If it takes longer the outcome will be discarded.

If the code that you end up running accepts a CancellationToken as well, pass the one in the example as well to be able to really cancel the operation. Otherwise you cannot actually cancel the operation but you can discard the result when it takes longer than 250ms.

By using a Timer you can take the same approach. In kind of pseudo code:

const int MaxAllowTimeInMs = 250;

void Main()
{
    var timer = new  DispatcherTimer();
    timer.Interval = TimeSpan.FromMilliseconds(MaxAllowTimeInMs);
    timer.Tick += async (s, e) =>
    {
        using(var cts = new CancellationTokenSource())
        {
            try
            {
                cts.CancelAfter(MaxAllowTimeInMs);
                await MyTask(cts.Token);
            }
            catch (OperationCanceledException)
            {
                // MyTask took longer than 205ms
            }
        }
    };
    timer.Start();
}

async Task MyTask(CancellationToken ct)
{
    // Simulate some work
    ...
    ct.ThrowIfCancellationRequested();
}
Peter Bons
  • 26,826
  • 4
  • 50
  • 74