C # Yield освобождает блокировку? - PullRequest
31 голосов
/ 05 января 2011

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

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 останется заблокированным на протяжении всей итерации?

Ответы [ 4 ]

20 голосов
/ 05 января 2011

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

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

  1. Когда итератор приостановлен и Disposeвызываемые блоки finally в области видимости в точке приостановки будут запускаться
  2. Когда итератор работает, а код в противном случае запускает finally запуск блока finally.
  3. Когда итератор встречает оператор yield break, блоки finally в области видимости в точке yield break будут запускаться
13 голосов
/ 05 января 2011

Блокировка преобразуется в try / finally (обычное c #)

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

«foreach» автоматически вызывает Dispose (), поэтому, если вы используете «foreach» (или обычный LINQ и т. Д.), Он будет получить разблокирован.

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

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

5 голосов
/ 05 января 2011

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

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

2 голосов
/ 05 января 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...