Repeating Task.Delay causes a large delay

hirossyihirossyi Member
edited February 2018 in Xamarin.iOS

I'm developing Xamarin.Mac(Cocoa) using C#. I want to develop application waiting seconds sometimes, so I developed waiting function using Task.Delay.
But repeating Task.Delay causes a large delay.

This is a test code for Cocoa.

using System;
using System.Threading.Tasks;
using System.Threading;
using AppKit;
using Foundation;

namespace TaskDelayTest
{
    public partial class ViewController : NSViewController
    {
        public ViewController(IntPtr handle) : base(handle)
        {
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
        }

        public override NSObject RepresentedObject
        {
            get
            {
                return base.RepresentedObject;
            }
            set
            {
                base.RepresentedObject = value;
            }
        }

        async partial void Execute(AppKit.NSButton sender)
        {
            while (true)
            {
                int millisecond = 1000;
                Console.WriteLine($"WaitTime : {millisecond}");
                var startDate = DateTime.Now;
                await Task.Delay(millisecond).ConfigureAwait(false);

                var endDate = DateTime.Now;
                var diff = (endDate - startDate);
                Console.WriteLine($"WaitEnd   : {endDate.ToString("yyyy/MM/dd HH:mm:ss.fff")}");
                Console.WriteLine($"DiffTime : {diff.TotalMilliseconds}");
            }
        }
    }
}

Console result:
.
.
.
(Repeating a lot of times)
.
.
.
WaitTime : 1000 WaitEnd : 2018/02/01 01:00:25.784 DiffTime : 1000.192
WaitTime : 1000 WaitEnd : 2018/02/01 01:00:26.784 DiffTime : 1000.193
WaitTime : 1000 WaitEnd : 2018/02/01 01:00:27.785 DiffTime : 1000.232
WaitTime : 1000 WaitEnd : 2018/02/01 01:00:28.785 DiffTime : 1000.462
WaitTime : 1000 WaitEnd : 2018/02/01 01:00:39.785 DiffTime : 10999.643
WaitTime : 1000 WaitEnd : 2018/02/01 01:00:42.259 DiffTime : 2473.116
WaitTime : 1000 WaitEnd : 2018/02/01 01:00:43.582 DiffTime : 1322.505
WaitTime : 1000 WaitEnd : 2018/02/01 01:00:44.582 DiffTime : 1000.173
WaitTime : 1000 WaitEnd : 2018/02/01 01:00:45.582 DiffTime : 1000.147
WaitTime : 1000 WaitEnd : 2018/02/01 01:00:48.973 DiffTime : 3389.941
WaitTime : 1000 WaitEnd : 2018/02/01 01:00:49.973 DiffTime : 1000.186
WaitTime : 1000 WaitEnd : 2018/02/01 01:00:50.973 DiffTime : 1000.186
WaitTime : 1000 WaitEnd : 2018/02/01 01:00:52.083 DiffTime : 1108.889
.
.
.

Sometimes very very delayed. This did not happen on Windows. It only occurred with Xamarin.Mac.
And I have to develop as asynchronous function because it's cocoa application. If I develop as not async function, it locks UI.

Answers

  • ChrisHamonsChrisHamons USForum Administrator, Xamarin Team Xamurai

    So waking up significantly after your scheduled wake up time is not unexpected when writing code of that nature. Task.Delay(millisecond) is saying "wait at least X ms", not "wake up me at this time".

    You may want to read this documentation: https://developer.xamarin.com/guides/ios/application_fundamentals/threading/

    But to answer the root question, you need to integrate more closely with the underlying OS. NSTimer doen't have real-time guarantees, and can shift your wake up times when your device is on batteries, but will likely get you roughly what you want:

                    NSButton b = new NSButton (new CGRect (0, 0, 100, 50));
                b.Activated += (o, e) =>
                {
                    Console.WriteLine($"Starting : {DateTime.Now}");
                    NSTimer.CreateRepeatingScheduledTimer (1, (obj) => {
                        Console.WriteLine($"Proc : {DateTime.Now}");
                    });
                };
                View.AddSubview (b);
    

    Since Task.Delay gets scheduled on the thread pool, it has higher jitter if you need that sort of precision.

  • HaoyuanTangHaoyuanTang USMember ✭✭
    edited July 2018

    Hi, I encounter the same problem, when I repeat Task.Delay(100) or jus Thread.Sleep(100) in an just empty Xamarin.Mac app, it will delay even up tp 10s. Have you solved it yet?

    I also tried > @ChrisHamons 's solution by using NSTimer instead, but the same:
    delay time: 98
    delay time: 100
    delay time: 99
    delay time: 99
    delay time: 99
    delay time: 99
    delay time: 99
    delay time: 99
    delay time: 99
    delay time: 10098
    delay time: 6321
    delay time: 4078
    delay time: 1284
    delay time: 2869
    delay time: 1293
    delay time: 1078

  • HaoyuanTangHaoyuanTang USMember ✭✭

    It seems if the Xamarin.Mac app screens off for a while, the Task.Delay() or NSTimer() will not working correctly, but if I click the app to make it show on the top of the screen, the timing will becomes correct again. How weird it is. Will cocoa app also enter a status like sleeping?

  • HaoyuanTangHaoyuanTang USMember ✭✭

    Sorry for reposting so often, but I think I already figured out what the issue is. MacOS will enable 'App Nap' automatically, so if the app is not on the topmost foreground, it will take a nap eventually and the timer will become unpredictable, that is the reason why Task.Delay() or other timing functions do not work. There is an API can control this behaviour.
    https://stackoverflow.com/questions/33558194/disable-app-nap-for-mono-mac-application
    The accepted answer works like a charm.

    Not sure whether yours is due to the same issue, hope it will be helpful for anyone who suffer from this problem.

Sign In or Register to comment.