It's not really as complicated as it sounds.
The main thread spawns a number of auxiliary threads into a 'thread pool' when the game starts, equaling the number of available cores.
In addition you create a task queue - A list of dwarfs and their destinations that require pathing.
You also create a condition variable; a lock that guarantees that only one thread is accessing the queue at a time.
Now, the aux threads go to sleep. The main thread runs all game logic, and when it gets to the pathfinding phase, it gathers all the dwarfs and their destinations to the queue we created, then signals the condition variable to wake up all the threads. The main thread itself goes to sleep.
The aux threads, woken, access the queue in turn, taking a dwarf, removing it from the queue, and pathing its path. Then they return to the condition variable - If the queue is empty, all work is done, and they go to sleep. The last aux thread that completes its task (keep count how many are active) signals the main thread that the job is done.
From here the main thread resumes game logic that follows.
Since the map data array is only read during this operation, there is no need to manage thread access to it. The dwarfs don't need to be locked either, since each thread only works with its own dwarf, whose access is managed by the queue.