Подтвердить что ты не робот

Получает ли С# Yield блокировку?

У меня есть следующий метод:

public static IEnumerable<Dictionary<string, object>> GetRowsIter
   (this SqlCeResultSet resultSet)
{
    // Make sure we don't multi thread the database.
    lock (Database)
    {
        if (resultSet.HasRows)
        {
            resultSet.Read();

            do
            {
                var resultList = new Dictionary<string, object>();
                for (int i = 0; i < resultSet.FieldCount; i++)
                {
                    var value = resultSet.GetValue(i);
                    resultList.Add(resultSet.GetName(i), value == DBNull.Value 
                                                                  ? null : value);
                }
                yield return resultList;
            } while (resultSet.Read());
        }
        yield break;
    }

Я просто добавил lock(Database), чтобы попытаться избавиться от некоторых проблем с совместимостью. Мне любопытно, будет ли yield return освобождать блокировку на Database, а затем снова заблокировать, когда она будет продолжена для следующей итерации? Или будет Database оставаться заблокированным в течение всей продолжительности итерации?

4b9b3361

Ответ 1

Нет yield return не приведет к тому, что блокировки будут освобождены/разблокированы. Оператор lock будет расширяться до блока try / finally, и итератор не будет рассматривать это иначе, чем явный try / finally в методе итератора.

Детали немного сложнее, но основные правила, когда блок finally запускается внутри метода итератора,

  • Когда итератор приостановлен и Dispose называется блоками finally в области видимости в точке приостановки, будет выполняться
  • Когда итератор запущен, и в противном случае код запускает finally блок finally.
  • Когда итератор встречает оператор yield break, теги finally в области в точке yield break будут запускаться

Ответ 2

Замок переводится как try/finally (normal С#)

В блоках Iterator (aka yield) "finally" становится частью реализации IDisposable.Dispose() перечислителя. Этот код также вызывается внутренне, когда вы используете последний из данных.

"foreach" автоматически вызывает Dispose(), поэтому, если вы потребляете "foreach" (или regualar LINQ и т.д.), он будет разблокирован.

Однако, если вызывающий использует GetEnumerator() напрямую (очень редко), а doesnt читает все данные, а не выполняет вызов Dispose(), тогда блокировка не будет быть выпущен.

Мне нужно было бы проверить, есть ли или получает финализатор; он может быть выпущен GC, но я бы не стал делать ставки на него.

Ответ 3

Объект базы данных будет заблокирован до завершения итерации (или итератора).

Это может привести к чрезмерным периодам блокировки, и я бы рекомендовал не делать этого так.

Ответ 4

Блокировка остается в силе, пока вы не выйдете из области блокировки(). Уступчивость не делает этого.