Home > Software engineering >  Task.Run() Delay, when first called
Task.Run() Delay, when first called

Time:09-29

I am running a Thread which get called in a static interval. In this Thread I am running several Tasks (20-200). All this works fine, but when the Thread gets called the first time, it takes like ~1 sec for one Tasks to start. As soon as the while loop is in the second loop or when the Thread stops, and gets called a second time, the problem is gone.

public static async void UpdateThread()
{          
     while(!stop)
     {       
         foreach (DSDevice device in DSDevices)
         {
            var task = Task.Run(() =>
            {
                // Delay is measured here
                // Do Stuff
            });
        }
        //No Delay
        await Task.WhenAll(tasks);
        Thread.Sleep(Sleeptime);
    }
}

CodePudding user response:

The Task.Run runs the code on the ThreadPool, and the ThreadPool creates initially a limited number of threads on demand. You can increase this limit with the SetMinThreads method:

ThreadPool.SetMinThreads(200, 200);

...but check out the documentation before doing so. Increasing this threshold is not something that you should do without thinking. Having too many ThreadPool threads defeats the purpose of having a pool in the first place. Think whether it's better to have a dedicated thread per device, for the whole life-time of the program.

As a side note, if I was in your shoes I would not parallelize the processing of the devices by creating tasks manually. I would use the Parallel.ForEach method, which exists for exactly this kind of job. As a bonus it allows to control the degree of parallelism, either to a specific number or to -1 for unlimited parallelism:

public static async Task MonitorDevicesPeriodicAsync(
    CancellationToken stoppingToken)
{
    while (!stoppingToken.IsCancellationRequested)
    {
        Task delayTask = Task.Delay(MonitorDevicesPeriodMilliseconds);
        await Task.Run(() =>
        {
            ParallelOptions options = new() { MaxDegreeOfParallelism = -1 };
            Parallel.ForEach(DSDevices, options, device =>
            {
                // Do Stuff with device
            });
        });
        await delayTask;
    }
}

The Parallel.ForEach invokes also the delegate on the ThreadPool (by default), and it can saturate it as easily as the await Task.WhenAll(tasks) approach, so you might need to use the ThreadPool.SetMinThreads method as well.

Three more off topic suggestions: prefer async Task over async void. Async void is intended for event handler only. Also use a CancellationToken for stopping the while loop instead of a non-volatile bool stop field. In a multithreaded environment, it's not guaranteed that the mutation of the field from one thread will be visible from other threads. Alternatively declare the field as volatile. Finally use the Task.Delay instead of the Thread.Sleep, create the Task.Delay task at the start of the iteration and await it at the end, for a stable periodic invocation.

  • Related