The internal implementation can vary considerably; there are a number of approaches, based on cost, performance, etc goals.
The simplest choice is a priority arrangement, where the ports are numbered. and the memory controller attends to requests in priority order, using an arbiter to deal with requests that arrive simultaneously.
If there are multiple banks in the memory, then as long as access requests are spread out fairly evenly across them, it may be possible to perform memory cycles to different clients on separate ports simultaneously. (This approach was used on the PDP-10 and Honeywell 6000 systems.)
For small multi-port memories, it is possible to replicate the memories, so no read contention ever happens (since each port has its own private copy of all the data). If more than one port can write, however, contention may arise in the writing.
The topic remains an area of research to the current day.