For making a scalable multiuser server in C#, which of these two types of servers are more efficient? You can see an example of the async server using the begin* methods (like beginaccept, beginsend) here, and the threadpool implementation here.
I know what a threadpool is and how it works so I pretty much understand how that implementation works. What about the async server though? Does that spawn a thread for each and every send, receive, and connection event? How efficient is that vs a threadpool?
By efficiency I just mean a general balance of speed and memory use.
It has been suggested to me to use the begin() methods, but don't these create overhead when they spawn a new thread to take care of the send, receive, or connect event? Or do they end up using some kind of internal threadpool? If not, is there a way to make it so it does use a threadpool or should I just roll my own async socket server?
The TcpServer implementation is going to be better, as it's going to take advantage of I/O completion ports to perform the asynchronous work, while the threadpool implementation is generally going to have to burn a thread to wait on the connections that are being accepted.
Generally speaking, use the async methods on things like files, databases, sockets (things that have an underlying unmanaged implementation to them) as they will utilize I/O completion ports for their work, which is generally more efficient than moving the code to the thread pool. However, it is going to add complexity to your code, so that is something you have to account for.
If by scalable, you mean really large numbers of connections, you should also consider a number of other aspects beyond use of the thread-pool.
1) If you are using .NET3.5, consider SocketAsyncEventArgs rather than the BeginXXX/EndXXX. The approach is described in MSDN Magazine. These allow for more efficient dispatch and processing of the underlying async i/o.
2) Consider your memory usage very carefully. Byte[] buffers allocated for asynchronous i/o are pinned, forcing the GC to work around them during the compaction phase which in turn may result in fragmentation and OutOfMemoryExceptions under load. Also, you have consider the lower-level native issues of async i/o such as available memory in the non-paged pool.
Some of the available techniques are using a single shared zero-byte buffer for all pending receives, and using pooled buffers (from memory that does not compacted - either LOH or native heap) for I/O that will actually transfer data.
A better approach may be to use Jeffery Richter's AsyncEnumerator. It's part of the Wintellect PowerThreading Library. You can watch a video presentation of how it works over at Channel 9.
I'm using it in two projects, and the great thing about it is that it allows you to share the limited resources of the threadpool across many requests, so it doesn't tie up threads for each request. You get the best of both worlds.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With