2

I am in the process of updating an app that is going from supporting a single connection to being able to support numerous connections. I have figured that using the Task Parallel Library may be the best way to go about this in terms of creating and managing the multiple sockets there will be. As such, I have begun to write some code and have come across a possible issue while debugging. It seems that some of my tasks that I create to set up the connections never complete and am wondering if I am utilizing TPL correctly or if it is something else. If I debug the code, it appears as if only one task in the collection actually contains a Socket. Any assistance is appreciated. Here is the code from a sandbox console application.

Main:

        static void Main(string[] args)
        {
            Console.WriteLine("Creating connections.....");
            var sockets = CreateListeners(5);

            Console.WriteLine("Socket Info:");

            foreach (var socket in sockets)
            {                
                if (socket.Result != null)
                {
                    var con = socket.Result;
                    IPEndPoint ipep = (IPEndPoint)con.LocalEndPoint;
                    string port = ipep.Port.ToString();
                    Console.WriteLine("Socket #{0} - Listening on Port {1}:", socket.Id.ToString(), port);
                }
            }
            Console.WriteLine("Press any key to exit..");
            Console.ReadLine();
        }

Function that creates a list of Tasks:

private static List<Task<Socket>> CreateListeners(int numberToCreate)
            {
                var sockets = new List<Task<Socket>>();
                for (int n = 0; n < numberToCreate; n++)
                {
                    var currentSocket = Task<Socket>.Factory.StartNew(() => CreateSocketConnection(n));
                    sockets.Add(currentSocket);
                }

                return sockets;            
            }

Function that creates a single socket connection..

private static Socket CreateSocketConnection(int port)
            {

                try 
                {
                    IPAddress ipAddress = null;
                    IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
                    if (Socket.OSSupportsIPv6)
                    {
                        ipAddress = ipHostInfo.AddressList[1];
                    }
                    else
                    {
                        ipAddress = ipHostInfo.AddressList[0];
                    }


                    //Create a new connection on specified port
                    IPEndPoint endPoint = new IPEndPoint(ipAddress, port+6000);
                    Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    listener.Bind(endPoint);
                    //listener.Blocking = false;
                    listener.Listen(500);

                    return listener;

                }
                catch (Exception ex)
                {
                   throw ex;        
                }
                finally
                {

                }

                return null;            
            }
CheckRaise
  • 550
  • 2
  • 16
  • 1
    Nooooooooo not throw ex, bad, bad C# code : http://www.tkachenko.com/blog/archives/000352.html – Julien Roncaglia Dec 06 '11 at 20:30
  • 1
    This is just sandboxed code that would never be put into production. Thanks though. – CheckRaise Dec 07 '11 at 00:45
  • see that: http://stackoverflow.com/questions/5834755/turning-async-socket-parallel-and-not-only-concurrent-in-very-intensive-applicat –  Mar 01 '12 at 17:50
  • To simplify use it: http://stackoverflow.com/questions/11831844/unobservedtaskexception-being-throw-but-it-is-handled-by-a-taskscheduler-unobser –  Sep 13 '12 at 15:19

3 Answers3

2

Your problem seems to be that you're launching async tasks but not waiting for them to complete before you query their result.

After your CreateListeners call, you know that you've started 5 tasks. You let the task library decide about the parallelism or sequence.

Therefore, you need to call socket.Wait(); before if (socket.Result != null), because it very well may be null if it hasn't completed yet.

As VirtualBlackFox has stated, it may very well be executing them sequentially if deems appropriate, which could mean that when you start querying results, only one task has completed yet.

As a general answer, it doesn't seem overkill to me to use parallism for this operation. IO-type operations can generally benefit a lot from parallism since they involve substantial overhead.

CWilliams
  • 213
  • 1
  • 7
  • 1
    Adding socket.Wait() was what I ended up doing while waiting for responses. So I will mark yours as answer. However I think I have decided to forego the Task Factory and have just implemented a Parallel.For to loop and create the sockets. – CheckRaise Dec 07 '11 at 00:49
1

If you don't mark your tasks as LongRunning they use thread pool threads (Well in the current implementation) and the TPL decides when to really run them : It may run them all serially one after the other on the same thread if it want and in your case it won't be a good thing

Task<Socket>.Factory.StartNew(start, CancellationToken.None, 
    TaskCreationOptions.LongRunning, // The interpretation depends on scheduler
    TaskScheduler.Default // The default one place them in their own thread
    );

Also as said in my comment

throw ex;

is removing all the stacktrace information from your exception and when debugging you never want this, user either of theses :

throw; // To re-throw the same exception
throw new Exception("bla", ex); // To specify a new message, or change the
                                // exception type. A new stack trace is created
                                // but you can still access the original one
                                // using InnerException
Julien Roncaglia
  • 17,397
  • 4
  • 57
  • 75
0

This link has a nice write up on how to use async TCP, which uses the IO threadpool for callbacks.

http://msdn.microsoft.com/en-us/library/5w7b7x5f(v=VS.100).aspx

Since you want to bind to multiple ports, instead of a WaitOne, creating one for each socket and use a WaitAny. This way you can still have one thread listening.

From what I understand about async callbacks with IO completion ports, is don't do a lot of work in those calls backs. If you need to do any heavy lifting, offload to a task/thread.

Bengie
  • 1,035
  • 5
  • 10