Я пытаюсь реализовать потокобезопасный пул ресурсов, к которому можно быстро получить доступ и который получает только случайные обновления. Для этого я использую ReaderWriterLockSlim с методом расширения для входа и выхода из блокировки чтения. При профилировании использование потока оказалось неожиданно очень низким.
Я убедился, что запись в пул не производилась, поэтому функция EnterWriteLock() никогда не вызывалась.
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();
}
}
При профилировании я обнаружил, что большинство потоков проводят около 25% своего времени внутри EnterReadLock(). Внутренний сон этой функции занимает около 19%, а остальное время используется для активного вращения и других накладных расходов. Я бы ожидал, что EnterReadLock вообще не будет спать, а будет вращаться только в течение короткого времени. Есть ли способ повысить эффективность использования и сократить время ожидания?
Тонкие версии многопоточных компонентов используют спин-блокировки, которые работают быстро, но интенсивно используют ЦП. Если вы ожидаете ожидания любой продолжительности, используйте более старый ReaderWriterLock.
Если SpinLock не может получить данные в течение короткого периода времени, он переходит в спящий режим для экономии ресурсов ЦП. Если вы используете более старые типы, то вы ожидаете собственных мьютексов Windows, которые взаимодействуют с планировщиком задач (т.е. поток блокируется во время ожидания и разблокируется, когда ресурс освобождается). Это медленнее, но имеет меньшую нагрузку на систему.
Почему я должен ожидать сна? Я не ожидаю никакого сна, так как я не вызываю EnterWriteLock. Я бы ожидал, что EnterReadLock вообще не будет спать, а будет вращаться только в течение короткого времени. Не могли бы вы пояснить, почему EnterReadLock вызывает спящий режим, даже если ни один модуль записи не удерживает блокировку?