Rework work-stealing queue list
The current data structure used to store the list of local work-stealing queues is maintains a sparse array, where entries can be null, and where removals null out entries in an active array that could be being read by another thread concurrently. This necessitates that threads looking for work need to use volatile reads and null checks on each element. Further, because the array doubles in size when it grows, and never shrinks, we often end up having many empty slots that threads need to look at as they're looking for work.
It's actually relatively rare for threads to come and go. While the thread pool does take threads in and out of service, it only rarely actually terminates a thread or asks the OS for a new one, so it's relatively rare that threads are added/removed from the list. Given that, we can simply use immutable arrays, creating a new array of the exact right size whenever a thread is added or removed. Then iteration can be done without volatile reads and without null checks, because the contents of the array being read through won't change and won't ever be null.
Commit migrated from https://github.com/dotnet/coreclr/commit/
71bc68eced44a188ef51678b1d9c7337051cf3c3