I would use PLINQ for this and specify a max degree of parallelism like so:
I'm actually changing my answer on this one because I realized you just want to process a raw list directly and you're not doing any other filtering or mapping (Where/Select). In this particular case it would be better to use Parallel::ForEach and specify the MaxDegreeOfParallelism via ParallelOptions like so:
int myMaxDegreeOfParallelism = 4; // read this from config maybe
Parallel.ForEach(
list,
new ParallelOptions
{
MaxDegreeOfParallelism = myMaxDegreeOfParallelism
}
item =>
{
// ... your work here ...
});
Now, keep in mind, when you specify a max like this you prevent PLINQ from being able to use any more resources even if they're availabe. So if this ran on an 8 core machine, it would never utilize more than 4 cores. Conversely, just because you specified 4, doesn't mean 4 are guaranteed to execute simultaneously at any given time. It all depends on several heuristics that the TPL is using to be optimal.