Effectively, you can use three orthogonal mechanisms: 1) wait states threads enter due to your code, for example, when a thread calls a blocking call like System.Threading.WaitHandle.WaitOne, 2) frozen state, 3) wait state at a breakpoint, the mechanisms #2 and #3 applicable to the execution under the debugger.
Note that the method System.Threading.Thread.Suspend is similar (or even identical in effect) to Freeze, but is presently marked obsolete with the System.ObsoleteAttribute, so you cannot do this call. There were some essential reason for this decision; we can discuss them if you have further questions. However, you can still put a thread in a wait state forever using System.Threading.Thread.Sleep using infinite timeout value. It can be useful in some rare situations, but, naturally, not always safe.
That said, any of the wait or frozen states prevent a thread from continuing its execution. It is important to understand that a waiting thread does not consume any CPU time until something wakes it up. It is totally different from any kinds of spin waits. A thread yields to other threads and the operating system's scheduler don't schedule it back to execution until somethings tells the system to wake it up. It happens by 1) unblocking the call, for example, by signalling a WaitHandle mutex, lock, semaphore, blocking read calls, etc., 2) by the command Thaw, 3) by commanding continue at a breakpoint. But then the thread is waken up for continuing execution if two other conditions for its continuation are met.
Here, I don't discuss the aspects of the code controlling the execution of a thread in detail, all kinds of blocking calls, IPC, synchronization, and so on. This is a big separate topic. I just classified this aspect into one big category #1.
On top of it, you control the execution under the debugger. With Visual Studio, for example, you use breakpoints, Main menu > Debug > Start Debugging or Continue (F5), Main menu > Debug > Windows > Threads > context menu > Freeze or Thaw commands.