Усовершенствование пользовательского механизма предотвращения рекурсии скалы - PullRequest
2 голосов
/ 24 октября 2011

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

Вот моя попытка, пока здесь:

import scala.collection.mutable.{Set => MutableSet, HashSet => MutableHashSet }

case class RecursionException(uniqueID:Any) extends Exception("Double recursion on " + uniqueID)

object Locking {
  var locks:MutableSet[Any] = new MutableHashSet[Any]

  def acquireLock (uniqueID:Any) : Unit = {
    if (! (locks add uniqueID))
      throw new RecursionException(uniqueID)
  }

  def releaseLock (uniqueID:Any) : Unit = {
    locks remove uniqueID
  }

  def lock1 (uniqueID:Any, f:() => Unit) : Unit = {
    acquireLock (uniqueID)
    try {
      f()
    } finally {
      releaseLock (uniqueID)
    }
  }

  def lock2[T] (uniqueID:Any, f:() => T) : T = {
    acquireLock (uniqueID)
    try {
      return f()
    } finally {
      releaseLock (uniqueID)
    }
  }
}

и теперь для блокировки сегмента кода я делаю:

import Locking._

lock1 ("someID", () => {

  // Custom code here

})

Мои вопросы:

  1. Есть ли какой-нибудь очевидный способ избавиться от необходимости жесткого кодирования уникального идентификатора? Мне нужен уникальный идентификатор, который на самом деле будет использоваться всеми вызовами функции, содержащей заблокированный раздел (поэтому я не может иметь что-то вроде счетчика для генерации уникальных значений, если только у scala нет статических переменных функции). Я думал о чем-то
  2. Можно ли каким-то образом улучшить синтаксис функции anonymouse? В частности, что-то, что сделает мой код похожим на lock1 ("id") { /* code goes here */ } или любой другой более симпатичный вид.
  3. Немного глупо спрашивать на этом этапе, но я все равно спрошу - Я заново изобретаю колесо? (т.е. существует ли что-то подобное?)

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

Редактировать: Просто для ясности, этот проект предназначен для отладки ошибок и изучения scala. Он не имеет реального использования, кроме простого поиска ошибок кода во время выполнения (для обнаружения рекурсии, где это не должно происходить). Смотрите комментарии к этому посту.

Ответы [ 2 ]

4 голосов
/ 25 октября 2011

Не совсем уверен, к чему вы стремитесь, но несколько замечаний:

Во-первых, вам не нужно делать lock1 и lock2, чтобы различать Unit и другой тип.Единица - это правильный тип значения, для него тоже будет работать универсальный метод.Кроме того, вам, вероятно, следует использовать вызов по имени аргумента => T, а не функцию () => T, и использовать два списка аргументов:

def lock[T] (uniqueID:Any)(f: => T) : T = {
  acquireLock (uniqueID)
  try {
    f
  } finally {
    releaseLock (uniqueID)
  }
}

Затем вы можете вызвать с помощью lock(id){block} ивыглядит как общие инструкции, например, если или синхронизированы.

Во-вторых, зачем вам уникальный идентификатор, зачем делать Lock одиночным?Вместо этого сделайте Lock классом, у которого будет столько экземпляров, сколько у вас было бы идентификаторов.

class Lock {
  def lock[T](f: => T): T = {acquireLock() ...}
}

(Вы можете даже назвать свой метод блокировки apply, так что вы можете просто сделать myLock{....}вместо myLock.lock{...})

Многопоточность в стороне, теперь вам просто нужен логический var для acquire / releaseLock

Наконец, если вам нужно поддерживать многопоточность, вы должны решить, следует линесколько потоков могут войти в замок (это не было бы рекурсией).Если они могут, логическое значение должно быть заменено на DynamicVariable[Boolean] (или, возможно, java ThreadLocal, поскольку DynamicVariable - это InheritableThreadLocal, что вы можете или не можете хотеть).Если они не могут, вам просто нужно синхронизировать доступ в acquire / releaseLock.

2 голосов
/ 25 октября 2011

Есть ли какой-нибудь очевидный способ избавиться от необходимости жесткого кодирования уникального идентификатора?

Так как для того, что вы сказали в комментариях, это не такКод продукта, я полагаю, вы могли бы использовать свойство functions hashCode следующим образом:

def lock1 (f:() => Unit) : Unit = {
    acquireLock (f.hashCode)
    try {
      f()
    } finally {
      releaseLock (f.hashCode)
    }

Можно ли каким-либо образом подтвердить синтаксис функции anonymouse?

С вышеупомянутым изменением синтаксис должен быть красивее:

lock1 {

Если вы планируете сохранить идентификатор (если hashcode не подрезает его для вас)Вы можете определить свой метод следующим образом:

def lock1 (uniqueID:Any)(f:() => Unit) : Unit = {

Это позволит вам вызвать метод lock1 с помощью:

lock("foo") {

}

Приветствия!

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