Firstly, .Net does not choose which kernel this thread executes, as the OS does. If there is no other application with an intensive processor in the system, you can expect that each thread will run on a separate kernel. But if there is some other application, the OS may, for example, decide to start all your threads on the same core, switching between them.
And itβs even harder. A thread usually does not work on a single core; the OS constantly switches it from core to core. For example, view the following screenshot from the task manager, which shows the execution of a single-threaded application with heavy processor use.

You will notice that the only thread running on all of my 4 cores used approximately 25% of each core in the few seconds during which it ran.
.Net does not know about the CPU usage on your computer, therefore, it assumes that the optimal number of threads performing intensive work with the CPU matches the number of cores.
I do not know how PLINQ works, but I would not expect each core to produce exactly 1000/4 primes in your example. If one thread has already produced its share of primes, and another has not yet completed, it would be inefficient to let the first thread remain idle.
And yes, with I / O operations, the optimal number of threads does not depend on the number of cores, so you must set the degree of parallelism manually. (Do not forget that the optimal number of threads can be 1, harddisks are faster with sequential reads, rather than looking back and forth between many files.)
If you install WithDegreeOfParallelism(7) , it will definitely use 7 threads (again, without guaranteeing the number of cores). The OS will decide how to run these 7 threads on 4 cores. If all these threads have processor intensity, this is likely to give each thread something like 4/7 β 57% of the core. If they are associated with IO, it will execute the code for the thread that has just woken up (unlocked) on any core that is only available.
And WithDegreeOfParallelism() really sets the exact number of threads, not their maximum number, see Stephen Toub ParallelOptions.MaxDegreeOfParallelism vs PLINQs WithDegreeOfParallelism .