threads
In addition to what hplus0603 already said, you should generally never spawn threads, except when your program starts up. And then, you should spawn a fixed amount of them (typically equal to the number of CPU cores, or one less). Then assign tasks to the threads via one or several queues (lockfree ideally, but queues with a lock work just fine too, if you manage tasks properly). Note that when I say "task" then that does not mean something like "add two numbers", but something like "process 1,000 items".
The reason for that is that spawning threads is a lengthy, expensive operation which you do not want to do while receiving (or rather, not receiving, but dropping) UDP packets, and spawning a thread per task is generally a bad design, which is neither efficient nor scales well. Many threads means many context switches, which means a lot of CPU wasted for nothing.
You definitively want receiving UDP traffic to happen on one thread that does nothing else, since if you don't receive datagrams "immediately", your receive buffer will quickly fill up, and you will drop packets. So, you will definitively not want to do anything like reading a file, processing complicated game logic, or spawning threads in that same thread which handles the network. You don't need more than just one thread for that either, though. One thread is absolutely capable of doing that task (even with plain old blocking API calls and nothing special!).
ONE packet that has information about all players in the zone?
This depends. While that may be a very efficient solution (for some rare cases), it may not be the correct one. Every player in the zone (probably) does not see everything, but you are still sending every player the complete information. That may be acceptable, or it may be exploitable and therefore forbidding (depends on your game).
Also, not all information is equally important to each player. Depending on the amount of updates, you may have to send a considerable amount of data to every player. Bandwidth is not only limited (both at your end and at the other end!) but also costs money. You will therefore wish to reduce bandwidth by sending each player only
a) what they can actually see
b) what, in this subset, matters most
c) no more than a fixed so-and-so-much budget per second
It matters big time if someone who is 2 meters away makes a side step or changes clothes. This is immediately obvious. However, changing clothes may not be as important as pulling a gun.
It doesn't matter at all if someone 250 meters away makes a step or changes clothes. You likely won't notice at all.
Since the number of updates that you need to transmit scales at least quadratically with distance (according to the area of a disk for 2D/pseudo-3D, or if it's real 3D the volume of a sphere), you usually need to apply some "importance metric" that is somehow related to distance for each receiving user.
WinIOCP, ePoll or KEvent
This is an excellent tip for TCP, but less for UDP. With TCP, you have many sockets in an "unknown" state, but you can only do one thing at a time, so you need some special magic that tells you which one is ready so you can read from (or which overlapped operation has just finished).
Using UDP, you have a single socket, no more. And that single socket either has something to read, or it doesn't. Instead of blocking on a function that tells you "it's now ready" and then calling recvfrom, you can just as well block on recvfrom, which will conveniently return when something came in. Then push that packet on a queue (making the processing someone else's problem!) and immediately call recvfrom again.