1

I'm trying to implementing a threadsafe resource pool that is fast to access and only gets occasional updates. For that I use the ReaderWriterLockSlim with an extension method to enter and exit the read lock. While profiling the thread utilization was unexpectedly very low.

I verified that no write happened to the pool, so EnterWriteLock() was never called.

internal Mesh GetMesh(string name)
{
    using (CacheLock.Read())
    {
        if (MeshDictionary.TryGetValue(name, out var m))
        {
            return m;
        }
    }
    // <snip>
}
public static ReadLock Read(this ReaderWriterLockSlim l)
{
    return new ReadLock(l);
}
internal class ReadLock : IDisposable
{
    private readonly ReaderWriterLockSlim _lockObject;
    public ReadLock(ReaderWriterLockSlim l)
    {
        _lockObject = l;
        l.EnterReadLock();
    }
    public void Dispose()
    {
        _lockObject.ExitReadLock();
    }
}

While profiling I found that most threads spend about 25% of their time inside of EnterReadLock(). The internal sleep of that function takes about 19% and the rest is time used for spinning actively and other overhead. I would expect the EnterReadLock not to sleep at all and only spin for a short time. Is there a way to get better utilization and reduce wait time?

1 Answers1

3

The Slim versions of the threading components use spinlocks, which are fast but CPU intensive. If you are expecting a wait of any duration then use the older ReaderWriterLock.

Steve Todd
  • 1,250
  • 6
  • 13
  • Why should I expect sleeping? I don't expect any sleep since im not calling EnterWriteLock. **I would expect the EnterReadLock not to sleep at all and only spin for a short time.** Can you please clarify why EnterReadLock would call sleep even if no writer is holding a lock? – Valdiralita Apr 08 '19 at 15:54
  • 1
    If the SpinLock fails to acquire within a short period then it escalates to a sleep to save CPU resources. If you use the older types then you are waiting on native windows mutexes, which interact with the task scheduler (i.e. the thread becomes blocked while waiting, and unblocks when the resource is free'd). This is slower, but has less system overhead. – Steve Todd Apr 08 '19 at 17:18