Waiting for Task.Delay (foo); takes seconds instead of ms

Using a delay variable in Task.Delay randomly takes seconds instead of milliseconds in combination with an IO-like operation.

Code to play:

 using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ConsoleApplication { class Program { static void Main(string[] args) { Task[] wait = { new delayTest().looper(5250, 20), new delayTest().looper(3500, 30), new delayTest().looper(2625, 40), new delayTest().looper(2100, 50) }; Task.WaitAll(wait); Console.WriteLine("All Done"); Console.ReadLine(); } } class delayTest { private Stopwatch sw = new Stopwatch(); public delayTest() { sw.Start(); } public async Task looper(int count, int delay) { var start = sw.Elapsed; Console.WriteLine("Start ({0}, {1})", count, delay); for (int i = 0; i < count; i++) { var before = sw.Elapsed; var totalDelay = TimeSpan.FromMilliseconds(i * delay) + start; double wait = (totalDelay - sw.Elapsed).TotalMilliseconds; if (wait > 0) { await Task.Delay((int)wait); SpinWait.SpinUntil(() => false, 1); } var finalDelay = (sw.Elapsed - before).TotalMilliseconds; if (finalDelay > 30 + delay) { Console.WriteLine("Slow ({0}, {1}): {4} Expected {2:0.0}ms got {3:0.0}ms", count, delay, wait, finalDelay, i); } } Console.WriteLine("Done ({0}, {1})", count, delay); } } } 

This is also reported on connect .


Leaving the old question below for completeness.

I run a task that reads from a network stream, then lags for 20 ms and reads again (doing 500 views, it will take about 10 seconds). This works well when I read only 1 task, but strange things happen when I have several running tasks, and some with a long delay (60 seconds). My ms-delay tasks are suddenly halfway through.

I run the following code (simplified):

 var sw = Stopwatch(); sw.Start() await Task.Delay(20); // actually delay is 10, 20, 30 or 40; if (sw.Elapsed.TotalSeconds > 1) { Console.WriteLine("Sleep: {0:0.00}s", sw.Elapsed.TotalSeconds); } 

Fingerprints:

Sleep: 11.87s

(In fact, this gives a delay of 20 ms 99% of the time, they are ignored).

This delay is almost 600 times higher than expected. The same delay occurs simultaneously with three separate threads, and they all continue again at the same time.

A 60-second sleeping task wakes up as usual ~ 40 seconds after completing short tasks.

In half the cases, this problem does not occur. The other half has a sequential delay of 11.5-12 seconds. I would suspect a scheduling or pool thread problem, but all threads should be free.

When I pause my program during the stuck phase, the main stacktrace stack is on Task.WaitAll , 3 tasks are scheduled on await Task.Delay(20) , and one task is scheduled on await Task.Delay(60000) . There are also 4 more tasks. Waiting for these first 4 tasks, reporting things like β€œTask 24”: β€œTask 5313” (belongs to topic 0). ”All 4 tasks say that the wait task belongs to thread 0. There are also 4 ContinueWith tasks that I think can be ignored.

Scheduled tasks

Something else happens, like a second console application that writes to a network stream, but one console application should not affect another.

I absolutely do not understand this. What's happening?

Update:

Based on comments and questions:

When I run my program 4 times, it will hang 2-3 times for 10-15 seconds, 1-2 times it will work as usual (and will not print "Sleep: {0: 0.00} s".)

Thread.Count does go up, but it happens regardless of the hang. I just had a run where it did not hang, and Thread.Count started from 24, up to 40 seconds after 1 second, about 22 seconds, short tasks ended normally, and then Thread.Count slowly decreased to 22 slowly over the next 40 seconds.

Another code, the full code is located at the link below. Initial Clients:

 List<Task> tasks = new List<Task>(); private void makeClient(int delay, int startDelay) { Task task = new ClientConnection(this, delay, startDelay).connectAsync(); task.ContinueWith(_ => { lock (tasks) { tasks.Remove(task); } }); lock (tasks) { tasks.Add(task); } } private void start() { DateTime start = DateTime.Now; Console.WriteLine("Starting clients..."); int[] iList = new[] { 0,1,1,2, 10, 20, 30, 40}; foreach (int delay in iList) { makeClient(delay, 0); ; } makeClient(15, 40); Console.WriteLine("Done making"); tasks.Add(displayThreads()); waitForTasks(tasks); Console.WriteLine("All done."); } private static void waitForTasks(List<Task> tasks) { Task[] waitFor; lock (tasks) { waitFor = tasks.ToArray(); } Task.WaitAll(waitFor); } 

In addition, I tried replacing Delay(20) with await Task.Run(() => Thread.Sleep(20)) Thread.Count now goes from 29 to 43 and returns to 24, but it never hangs among several runes.

With or without ThreadPool.SetMinThreads(500, 500) , using TaskExt.Delay from noserati , it does not hang. (Nevertheless, even switching to a single line of code sometimes stops it from freezing, only to accidentally continue after restarting the project 4 times, but I tried it 6 times in a row without any problems).

I have tried everything above with and without ThreadPool.SetMinThreads so far, it never really mattered.

Update2: CODE!

+6
source share
1 answer

Without seeing more code, it is difficult to make all the assumptions, but I would like to generalize the comments, this may help someone else in the future:

  • We found that ThreadPool stuttering is not a problem here, since ThreadPool.SetMinThreads(500, 500) did not help.

  • Is there a SynchronizationContext anywhere in the workflow of your task? Put Debug.Assert(SyncrhonizationContext.Current == null) everywhere to check this out. Use ConfigureAwait(false) with each await .

  • Is there .Wait , .WaitOne , .WaitAll , WaitAny , .Result anywhere in your code? Any lock () { ... } constructions lock () { ... } ? Monitor.Enter/Exit or any other blocking synchronization primitives?

  • Regarding this: I have already replaced Task.Delay(20) with Task.Yield(); Thread.Sleep(20) Task.Yield(); Thread.Sleep(20) as a workaround that works. But yes, I continue to try to find out what is happening here, because the idea that Task.Delay (20) can shoot so far from the line makes it completely unsuitable.

    That sounds alarming. It is very unlikely that there is an error in Task.Delay , but anything is possible. For experimentation, try replacing await Task.Delay(20) with await Task.Run(() => Thread.Sleep(20)) , with ThreadPool.SetMinThreads(500, 500) still in place.

    I also have an experimental Delay implementation that uses the unamanaged CreateTimerQueueTimer API (as opposed to Task.Delay , which uses System.Threading.Timer , which in turn uses a managed TimerQueue ). It is available here as an entity . Do not set it as TaskExt.Delay instead of the standard Task.Delay . Timer callbacks are sent to ThreadPool , so ThreadPool.SetMinThreads(500, 500) should still be used for this experiment. I doubt it can make a difference, but I would be interested to know.

+2
source

Source: https://habr.com/ru/post/982984/


All Articles