Reputation: 77
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
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
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