3 votes

Task.Delay se termine plus tôt

J'ai observé, à la fois dans .NET Framework et .NET Core, que Task.Delay() semble terminer plus tôt qu'il ne le devrait. Habituellement, le retard est de l'ordre de 10 usecs, mais en de rares occasions, il peut atteindre quelques msecs. Considérez ce programme :

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("TaskDelayTest .NET Framework");
        while (true)
        {
            DateTime now = DateTime.UtcNow;
            TimeSpan wait = TimeSpan.FromMilliseconds(1000);
            DateTime then = now + wait;
            Task delay = Task.Delay(wait);
            delay.ContinueWith(Execute, then);
            Thread.Sleep(100);
        }
    }

    static void Execute(Task delay, object arg)
    {
        DateTime later = DateTime.UtcNow;
        DateTime then = (DateTime)arg;
        if (later < then)
        {
            Console.WriteLine("Early execute!!!!  {0:n0} ns", (then.Ticks - later.Ticks) * 100);
        }
    }
}

Je m'attendrais à ce que la ligne "Exécution anticipée" ne soit JAMAIS imprimée parce que Task.Delay devrait attendre au moins aussi longtemps comme paramètre de retard. Cependant, ce n'est pas ce que j'observe. Si vous laissez le programme s'exécuter suffisamment longtemps, il finit par afficher le message "Early execute". Ai-je mal interprété la spécification ?

TaskDelayTest .NET Core
Early execute!!!!  199,800 ns
Early execute!!!!  22,200 ns
Early execute!!!!  353,300 ns
Early execute!!!!  571,200 ns
Early execute!!!!  90,700 ns
Early execute!!!!  85,600 ns
Early execute!!!!  9,300 ns
Early execute!!!!  540,600 ns
Early execute!!!!  141,200 ns
Early execute!!!!  107,800 ns
Early execute!!!!  397,200 ns
Early execute!!!!  297,000 ns

9voto

Peter Duniho Points 21282

La variation des temps de réveil que vous observez est tout à fait conforme aux performances attendues sous Windows. Dans une attente de 1000 ms, vous voyez occasionnellement des réveils précoces, toujours autour de 1 ms ou moins.

Lorsque l'on examine des questions de ce type, il est utile et instructif de consulter la documentation relative aux fonctionnalités de niveau inférieur du système d'exploitation sur lesquelles reposent ces abstractions supérieures. Dans le cas de Task.Delay() cette dernière est basée sur un fil de minuterie qui maintient une file d'attente d'événements de minuterie, avec Thread.SleepEx() utilisé pour induire un délai entre les événements de la minuterie. Dans la documentation de cette fonction il est écrit :

Si dwMilliseconds est inférieur à la résolution de l'horloge système, le thread peut dormir moins longtemps que la durée spécifiée. Si dwMilliseconds est supérieur à un tick mais inférieur à deux, l'attente peut être comprise entre un et deux ticks, et ainsi de suite.

En d'autres termes, quel que soit le temps d'attente que vous avez spécifié, s'il n'est pas un multiple pair de la résolution de l'horloge système, vous pouvez obtenir des réveils plus tôt que le temps spécifié, dans la limite de la résolution de l'horloge système.

Pratiquement tous les mécanismes de synchronisation qui reposent sur le planificateur de threads vont avoir la même limitation, y compris Task.Delay() .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X