First, if you want to have absolutely reliable timing, then using Windows and .Net are not the right choices. Windows because it's not a real-time OS and it can choose different timing for your code based on other things happening in the system. .Net is not a good choice because it has non-deterministic timing thanks to the garbage collector.
But if you just want to make the timing somewhat more reliable, there are some things you can do.
If you want to run each thread on its own processor, you could manually create the Threads and then set ProcessThread.ProcessorAffinity for each thread, but getting the ProcessThread for a Thread is not that simple.
If you just want to make sure all the tasks start immediately (no matter what other tasks are currently running on the ThreadPool), you should create your own Threads too.
But if all you want is to make sure that there is at most one task per processor, just set MaxDegreeOfParallelism to Environment.ProcessorCount.