Допустимо ли использовать yield return внутри блокировки?

1,00
р.
Есть два вопроса к коду, приведенному ниже:
Допустимо ли использовать yield return внутри блокировки? Что произойдёт с блокировкой, когда мы будет крутить метод Get() в цикле foreach? public IEnumerable Get() { _lock.EnterReadLock() try { foreach (var item in _dictionary) { yield return new SomeObject(item.Key, item.Value) } } finally { _lock.ExitReadLock() } }
private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim()


Ответ
Ограниченно допустимо. Когда вы такую последовательность будете обходить в цикле foreach - то перед входом в цикл блокировка будет взята, а после выхода - освобождена. Также блокировка будет освобождена при любом прерывании цикла.
Однако, использование IEnumerable не ограничивается простыми циклами. При использовании блокировок всегда важно небольшое время удержания блокировки - а тут вы его растягиваете на не зависящее от вас время! Так можно легко и до взаимоблокировки доиграться.
Обычно лучше всего под блокировкой получить копию данных, и работать в дальнейшем уже с ней:
public IEnumerable Get() { IEnumerable> d
_lock.EnterReadLock() try { d = _dictionary.ToList() } finally { _lock.ExitReadLock() }
foreach (var item in d) { yield return new SomeObject(item.Key, item.Value) } }
Или даже вот так:
public IEnumerable Get() { _lock.EnterReadLock() try { return _dictionary.Select(item => new SomeObject(item.Key, item.Value)).ToList() } finally { _lock.ExitReadLock() } }