user2964559
user2964559

Reputation: 77

C# SocketAsyncEventArgsPool Stack Empty

Hy, I'm using the SocketAsyncEventArgsPool described on MSDN.

For each TCP Client i have an own pool(Stack) of 50 SocketAsyncEventArgs to write from the server to the Client. So, this works fine, but on restart the Client or the Server i have a Function that is sending many messages to the Client and for each message one SocketAsyncEventArgs is taken from my pool. When there are too many messages my Pool is empty and there is no free SocketAsyncEventArgs Object for sending und this message will not be send to the Client.

Is there any possibility to avoid this without increase my pool??? Thanks!!!

Upvotes: 2

Views: 1866

Answers (2)

NeddySpaghetti
NeddySpaghetti

Reputation: 13495

If you don't want to increase the size of your pool and assuming you are correctly returning each SocketAsyncEventArgs after use you can use a BlockingCollection to hold the required number of SocketAsyncEventArgs. The consumers will block when there are no more items to consume until an item is returned to the collection.

Update

Here is some sample code that creates a BlockingCollection of size 1 and fires off some consumers to process simultaneously. Each consumer takes an item from the collection to process it and in the mean time the others are blocked on Take until an item is added back to the collection.

When processing, you'll probably need to do that in a try/finally block to ensure the item is always added back after processing it if an exception gets thrown.

To close the collection you call CompleteAdding() and any blocked Take methods will throw a InvalidOperationException

public void RunConsumer(BlockingCollection<SocketAsyncEventArgs> collection, int consumerId)
{
    Task.Run( async () =>
    {
        Console.WriteLine("Consumer {0} waiting", consumerId);

        SocketAsyncEventArgs args = null;
        try
        {
            args = collection.Take();
            Console.WriteLine("Consumer {0} processing", consumerId);
            await Task.Delay(5000);
        }
        catch(ObjectDisposedException)
        {
           Console.WriteLine("Consumer {0} collection has been disposed", consumerId);
        }
        catch(InvalidOperationException)
        {
           Console.WriteLine("Consumer {0} collection has been closed", consumerId);
        }
        finally
        {
            // add the item back if collection hasn't been closed.
            if(args != null && !collection.IsAddingCompleted)
                collection.Add(args);
        }

        Console.WriteLine("Consumer {0} finished", consumerId);
    });
}

Usage

void Main()
{
    var collection = new BlockingCollection<SocketAsyncEventArgs>(1) { new SocketAsyncEventArgs() };

    RunConsumer(collection, 1);
    RunConsumer(collection, 2);
    RunConsumer(collection, 3);

    Thread.Sleep(9000);
    collection.CompleteAdding();
    Console.ReadLine();
}

Output

Consumer 1 waiting
Consumer 3 waiting
Consumer 2 waiting
Consumer 1 processing
Consumer 1 finished
Consumer 3 processing
Consumer 2 collection has been closed
Consumer 2 finished
Consumer 3 finished

Upvotes: 0

usr
usr

Reputation: 171246

If the pool is empty, just create a fresh object. This should be a rare event. Performance should not be impacted.

You can also dynamically increase the pool size by adding the freshly created object back to the pool when it is no longer used. That way the pool size keeps increasing until it satisfies all demand.

Upvotes: 2

Related Questions