views:

401

answers:

4

I want to create a high performance server in C# which could take about ~10k clients. Now i started writing a TcpServer with C# and for each client-connection i open a new thread. I also use one thread to accept the connections. So far so good, works fine.

The server has to deserialize AMF incoming objects do some logic ( like saving the position of a player ) and send some object back ( serializing objects ). I am not worried about the serializing/deserializing part atm.

My main concern is that I will have a lot of threads with 10k clients and i've read somewhere that an OS can only hold like a few hunderd threads.

Are there any sources/articles available on writing a decent async threaded server ? Are there other possibilties or will 10k threads work fine ? I've looked on google, but i couldn't find much info about design patterns or ways which explain it clearly

+1  A: 

You definitely don't want a thread per request. Even if you have fewer clients, the overhead of creating and destroying threads will cripple the server, and there's no way you'll get to 10,000 threads; the OS scheduler will die a horrible death long before then.

There are numerous articles online about asynchronous server programming in C# (e.g., here). Just google around a bit.

Marcelo Cantos
10k threads * 1kb stackspace = 10meg, not 10gig. Obviously you'll be rounded up to page granularity, though - and in reality, not just that, but dwAllocationGranularity (64kb). Still, 10k threads with minimal stacksize is ~625meg, not 10gig :) (this is excluding the non-stacksize per-thread overhead, though)
snemarch
+1 @snemarch. Must have been late. I've removed the stack size comments.
Marcelo Cantos
+3  A: 

You're going to run into a number of problems.

  1. You can't spin up 10,000 threads for a couple of reasons. It'll trash the kernel scheduler. If you're running a 32-bit, then the default stack address space of 1MB means that 10k threads will reserve about 10GB of address space. That'll fail.

  2. You can't use a simple select system either. At it's heart, select is O(N) for the number of sockets. With 10k sockets, that's bad.

  3. You can use IO Completion Ports. This is the scenario they're designed for. To my knowledge there is no stable, managed IO Completion port library. You'll have to write your own using P/Invoke or Managed C++. Have fun.

Kennet Belenky
While i agree 10k threads is not the way to go. A 32 bit windows can easily create 10k threads. http://blogs.technet.com/markrussinovich/archive/2009/07/08/3261309.aspx
Byron Whitlock
"A basic kernel stack is 12K on 32-bit Windows and 24K on 64-bit Windows. 14,225 threads require about 170MB of resident available memory," http://blogs.technet.com/markrussinovich/archive/2009/07/08/3261309.aspx
Byron Whitlock
Yes, good luck running a managed thread with basically zero stack space.
Kennet Belenky
I may be wrong about IO Completion ports requiring P/Invoke. It appears that the async IO built into System.Net.Sockets may take advantage of them already.
Kennet Belenky
What would be wrong to use the BeginXxx methods on sockets? To my understanding those use IO Completion ports. Fire up Reflector and search for the BaseOverlappedAsyncResult class, you'll see what I mean.
Lucero
@Bryon: That article you reference is for 14,000 threads that are permanently sleeping, each with bare miniumum stack space, not doing any work at all. To get 10,000 threads actually doing work is a MUCH different problem.
abelenky
@Lucero, as stated in my earlier comment, I believe that the BeginXxx methods do use IO Completion ports (but I don't have the motivation to find out for sure).
Kennet Belenky
@Kennet, I was researching the System.Net.Sockets IO Completion stuff while you wrote your comment, I only saw it later since I left thw browser open while researching.
Lucero
AFAIK "overlapped" async I/O is implemented using APC (http://msdn.microsoft.com/en-us/library/ms681951%28VS.85%29.aspx), not IOCP.
snemarch
I was thinking about creating one thread which accepts all the connections now. Create a ThreadPool to shoot the incoming Async Recieve and Write requests in. Would that be a good idea?
mark_dj
@mark_dj Yup, that'll probably work. I'm sure you'll run into problems, but that's what programming is about (handling 10k TCP connections is not an easy task. Google "C10K" for extensive discussions of the issues). You can use one thread to handle the Accepts, but you don't need to. The API has BeginAccept/EndAccept methods.
Kennet Belenky
Alright fired up 18k sockets.. worked fine however sockets weren't doing much ^_^'. What would be the best way to determine if a client disconnects? BeginWrite/BeginRead only fire their call back when they actually got data to read/write.. So i would need to send some garbage data now and then?I also hit a test with 6k sockets writing "hello" to my server, which took about a few seconds. Not a bad thing.. I accomplished it with BeginRead from TcpClient.GetStream() and ThreadPool.QueueUserWorkItem.
mark_dj
+1  A: 

You want to look into using IO completion ports. You basically have a threadpool and a queue of IO operations.

I/O completion ports provide an efficient threading model for processing multiple asynchronous I/O requests on a multiprocessor system. When a process creates an I/O completion port, the system creates an associated queue object for requests whose sole purpose is to service these requests. Processes that handle many concurrent asynchronous I/O requests can do so more quickly and efficiently by using I/O completion ports in conjunction with a pre-allocated thread pool than by creating threads at the time they receive an I/O request.

Byron Whitlock
+2  A: 

The way to write an efficient multithreaded server is to use I/O completion ports (using a thread per request is quite inefficient, as @Marcelo mentions).

If you use the asynchronous version of the .NET socket class, you get this for free. See this question which has pointers to documentation.

Timores