Fix GCHandle race in WeakStringCache.Concurrent (#5455)
Fix GCHandle race in WeakStringCache.Concurrent (#5455)

I haven't been able to reproduce #5442 on ARM64 but code inspection revealed a potential race between one thread dereferencing a `GCHandle` and another thread freeing it. The exception in #5442 does not match what I'd expect to see in such scenario but lacking other cues there's a good chance that fixing it will resolve the issue.

The concurrent implementation of `WeakStringCache` runs scavenging in parallel with other threads accessing the cache so we must never assume that an entry fetched from the cache is valid unless it's removed from the cache at the same time. 

Changes in this PR:
- `GetOrCreateEntry()` now always performs two operations on the cache. Previously it was one to three operations depending on whether it was a cache hit or cache miss. With this change hits become slower. One alternative would be wrapping `Scavenge()` with an R/W lock which would have negative impact on concurrency (scavenging would be an exclusive operation and all other threads would wait for it to finish). Another alternative would be per-entry locking which would add memory overhead. We can still do millions of cache hits per second per core with this new code so it's very unlikely to be a bottleneck.
- `_scavengeThreshold` is set to a high number before scavenging starts in order to prevent multiple threads scavenging at the same time. It is not a true lock so races are possible and we are OK with that.
cdmihai/msbuild,AndyGerlicher/msbuild,mono/msbuild,cdmihai/msbuild,rainersigwald/msbuild,mono/msbuild,rainersigwald/msbuild,rainersigwald/msbuild,mono/msbuild,sean-gilliam/msbuild,cdmihai/msbuild,rainersigwald/msbuild,sean-gilliam/msbuild,AndyGerlicher/msbuild,AndyGerlicher/msbuild,sean-gilliam/msbuild,mono/msbuild,AndyGerlicher/msbuild,cdmihai/msbuild,sean-gilliam/msbuild