Можно ли использовать функцию блокировки для реализации поточно-пронумерованного перечисления? - PullRequest
1 голос
/ 14 мая 2010

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

В C # вы можете сделать следующее:

  private IEnumerable<KeyValuePair<K, V>> Enumerate() {
     if (_synchronize) {
        lock (_locker) {
           foreach (var entry in _dict)
              yield return entry;
        }
     } else {
        foreach (var entry in _dict)
           yield return entry;
     }
  }

Единственный способ, которым я нашел это в F #, - это использование монитора, например ::10000

    let enumerate() =
        if synchronize then
            seq {
                System.Threading.Monitor.Enter(locker)
                try for entry in dict -> entry
                finally System.Threading.Monitor.Exit(locker)
            }
        else seq { for entry in dict -> entry }

Можно ли это сделать с помощью функции блокировки? Или есть лучший способ сделать это в целом? Я не думаю, что возвращение копии коллекции для итерации будет работать, потому что мне нужна абсолютная синхронизация.

Ответы [ 2 ]

4 голосов
/ 15 мая 2010

Я согласен с kvb, что код подозрительный и что вы, вероятно, не хотите, чтобы удерживал блокировку. Однако есть способ написать блокировку более удобным способом, используя ключевое слово use. Стоит упомянуть об этом, потому что это может быть полезно в других ситуациях.

Вы можете написать функцию, которая начинает удерживать блокировку и возвращает IDisposable, которая снимает блокировку при ее удалении:

let makeLock locker =
  System.Threading.Monitor.Enter(locker) 
  { new System.IDisposable with  
      member x.Dispose() =
        System.Threading.Monitor.Exit(locker) }

Тогда вы можете написать, например:

let enumerate() = seq {
  if synchronize then       
    use l0 = makeLock locker
    for entry in dict do
      yield entry       
  else 
    for entry in dict do
      yield entry }

Это, по сути, реализация C #, подобная lock, с использованием ключевого слова use, которое имеет схожие свойства (позволяет вам что-то делать при выходе из области). Так что это намного ближе к исходной версии кода на C #.

4 голосов
/ 14 мая 2010

Я не думаю, что вы сможете сделать то же самое с функцией блокировки, так как вы пытаетесь уступить изнутри. Сказав это, это выглядит как опасный подход на любом языке, поскольку это означает, что блокировка может удерживаться в течение произвольного промежутка времени (например, если один поток вызывает Enumerate(), но не перечисляет весь путь до получающегося IEnumerable<_>, тогда замок будет продолжать удерживаться).

Возможно, имеет смысл перевернуть логику, предоставив метод iter в соответствии с:

let iter f =
  if synchronize then
    lock locker (fun () -> Seq.iter f dict)
  else
    Seq.iter f dict

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

EDIT

Вот пример кода, который может навсегда удерживать блокировку.

let cached = enumerate() |> Seq.cache
let firstFive = Seq.take 5 cached |> Seq.toList

Мы взяли блокировку, чтобы начать перечисление первых 5 элементов. Однако, мы не продолжили в остальной части последовательности, поэтому блокировка не будет снята (возможно, мы перечислим остальную часть пути позже, основываясь на отзывах пользователей или что-то в этом случае, и в этом случае блокировка будет окончательно снята) .

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

...