How are multithreaded game servers written?
If there are 4 threads, is there one thread running the game loop, and 3 accepting and processing requests? Also: is information sent from the thread running th开发者_Python百科e game loop?
Starkey already pointed out that it depends a whole lot on the precise design.
For instance, on games with many clients, you'd assign dedicated threads to handling input, but for games with a few clients (say <=16) there's no need for multiple threads.
Some games feature NPC's with considerable smarts. It may be smart to run those on their own threads, but if you have too many you'll need a threadpool so a bunch of NPC's can share a single thread.
If you've got a persistent world, you'll need to write out state to a hard disk somewhere (probably via a DB). Since that has serious latencies, you won't want to have a main game loop wait on that I/O. That will be another thread, then.
Finally, there's the question whether you even have a main game loop. Would a MMO have a single loop, or would you rather have many ?
The main key is to make sure your game logic is not affected by your threading model.
As such, most game servers look something like this:
main() {
gGlobalReadOnlyStuff = LoadReadOnlyStuff();
SpawnThreads(numCores); // could be another limiting resource...
WaitForThreadsToBeReadyToGo();
while(1) {
WaitForNetworkInput(networkInput);
switch(networkInput.msg) {
case ADMIN_THING: // start/stop websever, dump logs, whatever...
DoAdminThing(networkInput.params);
break;
case SPAWN_GAME: // replace 'game' with 'zone' or 'instance' as needed
idThread = ChooseBestThread(); // round robin, random, etc
PostStartGameMessageToThread(idThread, networkInput.msg);
break;
// ...
}
}
}
void ThreadUpdate() {
threadLocalStuff = LoadThreadLocalStuff();
SignalThreadIsReadyToGo();
while(1) {
lock(myThreadsMessageQueue);
// copy messages to keep lock short
localMessageQueue = threadsMessageQueue;
unlock(myThreadsMessageQueue);
foreach(message in localMessageQueue) {
switch(message.msg) {
case SPAWN_GAME:
threadLocalStuff.games.MakeNewGame(message.params));
break;
case ADMIN_THING__LET_EVERYONE_KNOW_ABOUT_SERVER_RESET:
...;
break;
// etc...
}
}
foreach(game in threadLocalStuff.games) {
game.Update(); // game will handle its own network communication
}
}
The two hard things then are 'coming up with a partition (game, zone, instance, whatever) appropriate for you game' and 'transitioning things (players, fireballs, epic lootz) across those boundaries' One typical answer is "serialize it through a database", but you could use sockets/messages/files/whatever. But yeah, where and how to make these partitions and minimizing what can go across the boundaries is intimately tied to your game design.
(And yes, depending on your setup, there are possibly a few 'shared' systems (logging, memory) that may need a multithreading treatment(or even better, just have one logger/heap per thread))
精彩评论