0

This question is a continuation to a previous question I've asked:

It takes more than a few seconds for a task to start running

I now know how exactly to reproduce this scenario. Task.Factory.StartNew is scheduled on the thread pool, so I'm logging the following (just before I invoke the Factory.StartNew):

        int workerThreads = 0;
        int completionPortThreads = 0;
        ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads);
        ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
        var tokenSource = new CancellationTokenSource();
        CancellationToken token = tokenSource.Token;
         //I HAVE A LOG HERE

        Task task = Task.Factory.StartNew(() =>
        {
          //I HAVE A LOG ALSO HERE, AND THAT'S HOW I KNOW,    
          //THE TASK INVOCATION IS DELAYED, AND THE DALAY IS NOT DUE TO MY CODE WITHIN THE TASK
           // Some action that returns a boolean - **CODE_A**
        }).ContinueWith((task2) =>
        {
            result= task2.Result;
            if (!result)
            {
                //Another action **CODE_B**
            }
        }, token);

When the bug is reproduced, I get 32767 as Max worker threads, and 32756 as available worker threads.

Now, there is something I don't understand. At least as I've understood, once the threadpool reaches its overload, the threadpool will stop creating new threads immediately. And that's probably the reason for the delay of my task (that starts after more than 5 seconds from the invocation of Factory.StartNew).

But when the delay occurs, I see that I have 32756 available worker threads in my threadpool, so why does the threadpool NOT use one of those 32756 available worker threads to start my task immediately?

The available threads are on the ThreadPool (I mean, I invoke ThreadPool.GetAvailableThreads), and Task.Factory.StartNew allocates a task from the threadPool. So, why am I getting this delay despite having available threads in threadpool?

Cod Fish
  • 917
  • 1
  • 8
  • 37
  • What does your code do and why do you think it's *StartNew* that's slow instead of say, the code itself? Have you tried profiling the code? Just *pause* during debugging and check the `Parallel Stacks` window. What does it show? You are asking people to guess. What's certain is that the thread pool isn't slow, unless you *flood* the CPU with tasks, wasting all available time on thread switching. After all, you can't run more threads than there are logical cores at the same time – Panagiotis Kanavos Dec 03 '18 at 08:26
  • @Panagiotis Kanavos I know it for sure, because I have a log just before the invocation of StartNew and at the beginning of it, before my code even runs (the code within the task). – Cod Fish Dec 03 '18 at 08:29
  • Did you read [Stephen Cleary: StartNew is Dangerous](https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html)? – Sir Rufo Dec 03 '18 at 08:31
  • @DimaK so you don't know at all. You are assuming. We can't even do that because you haven't posted anything. *You* can't know it's the code's fault because you haven't checked how many other tasks there are. Either post your code, or use the Parallel debug windows to check what's going on when the delay occurs. – Panagiotis Kanavos Dec 03 '18 at 08:31
  • 2
    It's not the MAX worker threads value you need to look at - it's the MIN value you get via `ThreadPool.GetMinThreads()`. The max value is the absolute maximum threads that can be active. The min value is the number to always keep active. If you try to start a thread when the number of active threads is less than max (and greater than min) you'll see a 2 second delay. – Matthew Watson Dec 03 '18 at 08:40
  • @MatthewWatson Finally, the answer I was looking for. Can you please post it as a replay so I could mark it as an answer? – Cod Fish Dec 03 '18 at 08:43
  • Possible duplicate of [C# System.Timers.Timer odd behavior?](https://stackoverflow.com/questions/53188758/c-sharp-system-timers-timer-odd-behavior) – mjwills Dec 03 '18 at 08:46

1 Answers1

3

It's not the MAX worker threads value you need to look at - it's the MIN value you get via ThreadPool.GetMinThreads().

The max value is the absolute maximum threads that can be active. The min value is the number to always keep active. If you try to start a thread when the number of active threads is less than max (and greater than min) you'll see a 2 second delay.

You can change the minimum number of threads if absolutely necessary (which it is in some circumstances) but generally speaking if you find yourself needing to do that, you might need to think about redesigning your multithreading so that you don't need to.

As the Microsoft documentation states:

By default, the minimum number of threads is set to the number of processors on a system. You can use the SetMinThreads method to increase the minimum number of threads. However, unnecessarily increasing these values can cause performance problems. If too many tasks start at the same time, all of them might appear to be slow. In most cases, the thread pool will perform better with its own algorithm for allocating threads. Reducing the minimum to less than the number of processors can also hurt performance.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • What is the information source of the 2 seconds delay you mentioned ? – Ali Ezzat Odeh Dec 04 '18 at 08:53
  • @AliEzzatOdeh It's mentioned in passing [here](https://learn.microsoft.com/en-us/previous-versions/windows/silverlight/dotnet-windows-silverlight/0ka9477y(v=vs.95)). Also see [here](https://channel9.msdn.com/Shows/Going+Deep/Inside-Windows-8-Pedro-Teixeira-Thread-pool) – Matthew Watson Dec 05 '18 at 12:19