I use some multithreading in my engine, and am intending to implement more as time goes on, so I might be able to offer some advice on how it can be done for other roguelikes. Hold onto your hats, wall of text incoming.
This is what you want to do.
How about handling user input, user interface and rendering in a separate thread than the game logic? That way pressing keys should feel responsive even if the game logic slows down? It would also free *some* load from thread/core that handles game logic? But maybe that's not how it works at all
Step One: Separate your UI and Game into two distinct projects and set them up so that each portion can run with its own thread. Basically like a server and client. To save you a lot of trouble down the road they shouldn't even be able to reference each other, but I have a 'Base' project which defines some common data structures that both can use (like World/Level/Tile etc), and an 'Application' project which is able to kick them both off. The UI client receives everything it needs to know from messages sent to it, and in turn sends commands which the server waits for, and when available acts out a turn and sends more update messages. This is how my latest 7DRL
Hellspace works and its a great first step because adding networking and running the server on a remote computer is very easy to implement from here. If the server is laggy, the client can still render and play the nice animations and whatever else.
Step two: You should start to think about how the turn-based nature of roguelikes isn't well suited for synchronous execution. Every actor takes its turn in a distinct queue, and you can't let actors further down the queue take their turn at the same time as the current because earlier actor's actions might effect later ones. You can't even guarantee the actors further down the queue will even be alive by their turn! So you'll have to decide exactly how (if at all) you want your game to work time-wise. Will it be real-time? Will every actor take their turn at once, with a post processing phase to resolve conflicts? This also might be a good time to note that dual core computers are very common at the moment, meaning adding more threads than the two from step one will not nessecarily improve things for a lot of people.
Step Three: This is the step I'm up to. I want to have easily un-pluggable queue types so that I can work with both a single threaded traditional roguelike queue but also swap it out for a different multi-threaded real-time queue if I want to make that kind of game. There's lots to read about real-time game design, but essentially on each game 'tick' all the actors can decide what to do next at once before time is advanced. To facilitate this, I'm separating the level structure into chunks (ala minecraft), and to perform an operation inside a chunk it needs to be locked first. To perform multi-chunk operations, they're locked in order of their Ids to ensure there's no deadlocks. I intend chunks to be large-ish because my previous efforts to synchronise at finer levels of detail have been fraught with enourmous levels of complexity and a horrendous amount of deadlocks. When it comes to threading, if you can't completely and undeniably separate the systems (as with the client/server) then you have to very very clearly isloate the area of the system you want without getting confused by the details.
The biggest slow down of pathfinding is when there is no actual path, because all (?) algorithms will have to exhaust the whole area. To solve this you have to maintain what areas are disjoint, which can also be parallellized.
My engine also has multi-threaded pathfinding: when a path is requested, the requesting thread will idle while two runner threads calculate the same path at once. One source to goal and the other goal to source - the first one to find a complete path kills the other and returns. This fixes the problem you describe when a monster in an open desert can't get into a small hut - One thread searches out the entire desert looking for a way in, but the search from the hut outwards will very quickly find there's no path. I don't know how well this will work out with my implementation of step 3.