Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to track .Net thread pool usage?

AFAIK some methods in the .Net library are able to do I/O jobs asynchronously without consuming a thread from the pool.

If my information are correct the WebClient *Async methods do that.

I'd like to verify it by checking that effectively threads from the pool are not used during a download.

So my general question is : how can I monitor the current state of the thread-pool?

  • number of threads

  • number of busy threads

Is there some API (GetAvailableThreads?) or performance counters that would give this information?


EDIT: here are some more details

I'm writing a simple benchmark for educational purposes:

string[] urls = Enumerable.Repeat("http://google.com", 32).ToArray();
/*{
    "http://google.com",
    "http://yahoo.com",
    "http://microsoft.com",
    "http://wikipedia.com",
    "http://cnn.com",
    "http://facebook.com",
    "http://youtube.com",
    "http://twitter.com"
};*/

/*Task.Run(() =>
    {
        while (true)
        {
            int wt, cpt;
            ThreadPool.GetAvailableThreads(out wt, out cpt);
            Console.WriteLine("{0} / {1}", wt, cpt);
            Thread.Sleep(100);
        }
    });*/

WebClient webClient = new WebClient();
Stopwatch stopwatch = Stopwatch.StartNew();
foreach (string url in urls)
{
    webClient.DownloadString(url);
    Console.WriteLine("Got '{0}'", url);
}
stopwatch.Stop();

TimeSpan sequentialTime = stopwatch.Elapsed;

stopwatch.Restart();
CountdownEvent cde = new CountdownEvent(1);
foreach (string url in urls)
{
    cde.AddCount();
    webClient = new WebClient();
    webClient.DownloadStringCompleted += (_, __) =>
    {
        Console.WriteLine("Got '{0}'", __.UserState);
        cde.Signal();
    };
    webClient.DownloadStringAsync(new Uri(url), url);
}
cde.Signal();
cde.Wait();
stopwatch.Stop();

TimeSpan asyncTime = stopwatch.Elapsed;

stopwatch.Restart();
ThreadLocal<WebClient> threadWebClient = new ThreadLocal<WebClient>(() => new WebClient());
urls.AsParallel().WithDegreeOfParallelism(urls.Length).ForAll(url => threadWebClient.Value.DownloadString(url));
stopwatch.Stop();

TimeSpan PLinqTime = stopwatch.Elapsed;

Console.WriteLine("Sequential time: {0}.", sequentialTime);
Console.WriteLine("PLinq time: {0}.", PLinqTime);
Console.WriteLine("Async time: {0}.", asyncTime);

I'm comparing :

  • naive sequential loop
  • PLINQ loop
  • async I/Os

The interesting part are the last two.

I expect and try to prove that async I/Os are:

  • faster because they will create less pressure on the pool (less threads need to be created...)

  • lighter because they will consume less thread of the pool

My "benchmark" shows that it's faster and I guess that's because the pool does not need to allocate new threads for each request whereas with PLINQ each parallel request will block one thread.

Now I'd like to check the numbers about thread consumption.

The commented task was a poor attempt to monitor the pool. It may be the good starting point but until now the result are not really consistent with what I expect: it never displays that more than 3/4 threads are consumed, whereas I expect something like 32 threads busy.

I'm open to any idea to enhance it or better any other use-case that would clearly highlight the differences between the two approaches.

Hope this is clearer now, and sorry for not having provided the details sooner. :)

like image 540
Pragmateek Avatar asked Nov 21 '13 00:11

Pragmateek


1 Answers

The ThreadPool class provides the GetAvailableThreads method which "Retrieves the difference between the maximum number of thread pool threads returned by the GetMaxThreads method, and the number currently active." [1]: http://msdn.microsoft.com/en-us/library/system.threading.threadpool.getavailablethreads%28v=vs.110%29.aspx

You can capture the ratio thustly:

        int workerThreads;
        int completionPortThreads;
        ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
        Console.WriteLine("{0} of {1} threads available", workerThreads, completionPortThreads);
like image 80
JTW Avatar answered Oct 13 '22 22:10

JTW