# AI multithreading [closed]

MMO game. ~5000 (fake) players should be controlled by the server. Many of them have different classes -> different behavior. They should make some random pauses while doing some actions (~500-1500 ms) before attacking the next monster or after picking up a drop and so on. Also they should constantly checking the environment for aggressive monsters, other players. etc.

My question is, should I create a diff. thread for each player (looks not the best idea) or use another design strategy?

• If you want your server to stop responding, I highly recommend creating 5000 threads.
– DH.
Mar 3 '16 at 11:24
• Ask yourself how many of the actions that those 5000 fake players will actually be noticeable by your real players, and focus on those - i.e. if two AI's are doing X far away from any actual players, just calculate what would of happened if/when the players actually get in a position that would be affected by the action or notice them doing it Mar 3 '16 at 14:37
• Regarding your recent edit, see this question.
– user1430
Mar 3 '16 at 23:16

## 2 Answers

Implementing logic by threads is almost always a very bad idea. Multi-threaded programming is difficult! You will run into race conditions and deadlocks resulting in impossible to reproduce bugs.

There is usually only one justifiable reason to use multithreading, and that's performance: A program can sometimes be made faster by sharing work between CPU cores by running one thread per available core. But when you create more threads than you have cores, you usually achieve the opposite effect, because creation and management of threads is a quite expensive operation in itself.

Instead of running a separate thread per entity, have the main loop of your gameserver process all of them in order:

for (FakePlayer p: fakePlayers) {
p.update();
}


If you want the object to perform an action every n game-ticks (you should really measure time in game-ticks, not seconds), it should have an internal counter when it performed the last action and check that counter in their update-function.

When you implement this and notice that the performance is too slow and after profiling it you found out that the bottleneck is the CPU, you might consider to parallelize the processing of your game objects into multiple threads. A good tool for this is to use a ThreadPool because it will automatically assign tasks to a number of threads in a smart way without you having to manage threads yourself. Call awaitTermination after passing all your update tasks to it to make sure the procession is finished before you continue with your main loop.

for (FakePlayer p: fakePlayers) {
threadPool.submit(p -> p.update());
}
threadPool.awaitTermination();


And now for what's so hard about this: If you decide to do this and the fakePlayers.update methods read and write any data shared between them, you need to make sure these data structures are "thread-save". To understand all the ramifications of this, you should read and understand the official Java lesson about concurrency. Yes, all of it. Yes, from front to back. Otherwise you will get randomly occuring and impossible to reproduce bugs and your player-community will start a lynch mob.

A save way to do this without having to bash in your head too much about synchronization is to do only the decision finding (which should only require read-access to shared data) in parallel and then do the decision execution in serial order.

for (FakePlayer p: fakePlayers) {
threadPool.submit(p -> p.thinkAboutNextMove());
}
threadPool.awaitTermination();

for (FakePlayer p: fakePlayers) {
p.performNextMove();
}

• For the last bit of code you can implement performNextMove so it will only affect itself. then you can do them in parallel as well. For when multiple entities interact it requires that both parties discover what the other will do independently. Mar 3 '16 at 13:35
• @ratchetfreak I assumed that performing the move will involve interactions with other game objects (like damaging them) and with the game world as a whole (blocking different paths). Executing these in parallel without rigourous synchronization measures would result in unpredictable behavior. Mar 3 '16 at 13:41
• @phillip, That's why I said that it needs to discover the opposing entities' action independently and reliably and then make sure it doesn't create an impossible situation. It'll do double work but be entirely in parallel. Mar 3 '16 at 13:49

Create a thread for each player is an overkill, 5000+ threads will trash your server performance.

My suggestion is to create a job queue where you put all your 5000 players state then you can launch a small number of thread, depending on your server architecture, that will execute a simple loop like this.

while(true){
dequeueAIState();
updateAIState();
enqueueAIState();
}


This is not difficult to implement and will maximize parallelism avoiding an excessive number of thread.

Bonus point is that it's scalable, you can change the number of thread every time you want.