Search code examples
c#multithreadingthreadpoolasyncsocketsocks

Why checking proxies with ThreadPool takes much more time than doing the same without ThreadPool?


Using this post I wrote code that checks 200 proxies for example. The timeout for a socket is 2sec. Everything is working, but the problem that Code #1 takes more than 2minutes to check 200 proxies limited to 2sec timeout. But with Code #2 it takes 2sec to check 200 proxies and it would take also 2sec to check 1000 proxies with Code #2.

Code #1 uses ThreadPool. Code #1 opens proxyCount sockets, goes to Sleep for 2sec and than checks what succeeded. It takes 2sec exactly.

So where is the problem in Code #1? Why ThreadPool with minimum 20 threads are much much slower than doing it without threads?

Code #1

int proxyCount = 200;  
CountdownEvent cde = new CountdownEvent(proxyCount);     
private void RefreshProxyIPs(object obj)
{     
    int workerThreads, ioThreads;
    ThreadPool.GetMinThreads(out workerThreads, out ioThreads);
    ThreadPool.SetMinThreads(20, ioThreads);

    var proxies = GetServersIPs(proxyCount);
    watch.Start();
    for (int i = 0; i < proxyCount; i++)
    {
        var proxy = proxies[i];
        ThreadPool.QueueUserWorkItem(CheckProxy, new IPEndPoint(IPAddress.Parse(proxy.IpAddress), proxy.Port));
    }
    cde.Wait();
    cde.Dispose();
    watch.Stop();
}

private List<IPEndPoint> list = new List<IPEndPoint>();
private void CheckProxy(object o)
{
     var proxy = o as IPEndPoint;
     using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
     {
         var asyncResult = socket.BeginConnect(proxy.Address, proxy.Port, null, null);
         if (asyncResult.AsyncWaitHandle.WaitOne(2000))
         {
             try
             {
                 socket.EndConnect(asyncResult);
             }
             catch (SocketException)
             {
             }
             catch (ObjectDisposedException)
             {
             }
         }
         if (socket.Connected)
         {
             list.Add(proxy);
             socket.Close();
         }
     }
     cde.Signal();
}

Code #2

int proxyCount = 200;
var sockets = new Socket[proxyCount];
var socketsResults = new IAsyncResult[proxyCount];
var proxies = GetServersIPs(proxyCount);
for (int i = 0; i < proxyCount; i++)
{
      var proxy = proxies[i];
      sockets[i] = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
      socketsResults[i] = sockets[i].BeginConnect(IPAddress.Parse(proxy.IpAddress), proxy.Port, null, proxy);             
}
Thread.Sleep(2000);
for (int i = 0; i < proxyCount; i++)
{
     var success = false;
     try
     {
         if (socketsResults[i].IsCompleted)
         {
              sockets[i].EndConnect(socketsResults[i]);
              success = sockets[i].Connected;
              sockets[i].Close();
         }

         sockets[i].Dispose();
     }
     catch { }

     var proxy = socketsResults[i].AsyncState as Proxy;
     if (success) {  _validProxies.Add(proxy); }
}

Solution

  • The threadpool threads you start are just not very good tp threads. They don't perform any real work but just block on the WaitOne() call. So 20 of them start executing right away and don't complete for 2 seconds. the threadpool scheduler only allows another thread to start when one of them completes or none of them complete within 0.5 seconds. It then allow an extra one to run. So it takes a while before all the requests are completed.

    You could fix it by calling SetMinThreads() and setting the minimum to 200. But that's incredibly wasteful of system resources. You might as well call Socket.BeginConnect() 200 times and find out what happened 2 seconds later. Your fast version.