Создание GetEnumerator ThreadSafe - PullRequest
4 голосов
/ 08 мая 2011

Как именно работают счетчики - я знаю, что они создают конечный автомат за кулисами, но если я дважды вызову GetEnumerator, получу ли я два разных объекта?

Если я сделаю что-то подобное

  public IEnumerator<T> GetEnumerator()
  {
     yield return 1;
     yield return 2;
  }

Могу ли я получить блокировку в начале метода, и удерживается ли эта блокировка до тех пор, пока перечислитель не вернет значение null или пока перечислитель не получит GC'd?

Что произойдет, если вызывающая сторона сбросит перечислитель и т. Д.-

Полагаю, мой вопрос заключается в том, как лучше всего управлять блокировкой при работе с перечислителем

Примечание: клиент не может отвечать за синхронизацию потоков - класс должен быть внутренним

И, наконец, приведенный выше пример упрощает задачу: операторы yield делают немного больше, чем я показал :)

Ответы [ 2 ]

8 голосов
/ 08 мая 2011

Да, каждый вызов вашего GetEnumerator() метода создаст новый объект (новый конечный автомат).

Вы можете получить блокировку внутри блока итератора, но помнитечто ни один из кодов в вашем методе не будет вызываться до тех пор, пока вызывающий не вызовет MoveNext() в первый раз.

В общем, я бы посоветовал против с удержанием блокировки внутри блока итератора, еслина все возможно.Вы не знаете, что собеседник будет делать между звонками на MoveNext().До тех пор, пока они в какой-то момент избавятся от итератора, блокировка в конечном итоге будет снята, но это все равно означает, что вы зависите от вызывающего.

Если вы можете дать нам больше информации о том, что вы 'пытаясь сделать, это помогло бы.Альтернативный дизайн, который может было бы легче понять, был бы:

public void DoSomething(Action<T> action)
{
    lock (...)
    {
        // Call action on each element in here
    }
}
2 голосов
/ 08 мая 2011

Как уже сказал Джон, трудно дать вам хороший ответ. Очевидный поиск в Google приводит к: http://www.codeproject.com/KB/cs/safe_enumerable.aspx

Идея заключается в том, чтобы заблокировать экземпляр IEnumerable в конструкции, которая имеет серьезные недостатки.

Следующая очевидная вещь - это изоляция, когда вы создаете копию своей структуры и перебираете копию. Это если наивно реализовано очень много памяти, но это может стоить того, если ваш набор данных относительно мал.

Лучше всего, если ваши данные неизменны, тогда у вас есть автоматическая защита потоков, но если вы привязаны к коллекции, число которых действительно изменяется, у вас есть изменяемая структура данных. Если бы вы могли изменить структуру данных в неизменную, все готово.

Поскольку блокировка данных на долгое время не очень хорошая идея, вы можете реализовать стратегии, обеспечивающие безопасность потоков, когда вы используете точную структуру данных и пример использования. Если вы, например, редко изменяете данные и часто перечисляете их, вы можете реализовать оптимистическое перечисление, которое читает перед началом перечисления счетчик записи вашей структуры данных и выдает результаты, как обычно. Если между ними происходит запись, вы можете выдать исключение, чтобы дать сигнал пользователю вашего перечислителя повторить попытку, пока он не преуспеет. Это работает, но передает делегату вашего перечислителя ответственность за то, что он должен повторить перечисление, пока оно не будет успешно выполнено.

...