2013-04-02 1 views
1

일부 미래를 묶는 우아한 솔루션을 찾는 데 어려움이 있습니다. 내가 구현하기 위해 노력하고있어 방법 (캐시의 그것의 일부) 다음과 같습니다"롤백"코드로 미래를 편평하게 매핑하십시오.

def acquire(key: A, producer: => Future[B]): Future[B] 

그리고 알고리즘은 약이있다 : 키 즉시 런타임 예외를 통해 잠겨

  • 경우 (Future.failed 사용하는 사전이있을 것입니다?)
  • 는 달리 future { getOrRefresh } 블록을 열고는 키
  • getOrRefresh를 검색하는 데 시간이 필요하거나 다음 있습니다 END_STRONG_1입니다 직선 B을 반환하기 때문에 미래 및 방법의 t는
  • 아니면 마지막 단계는 내가 미래 내부 에서 미래의 '병합'해야한다는 함축 producer

를 실행해야합니다. 즉, outer.flatMap을 수행 할 수 없으므로 전략은 Await을 사용하는 것입니다.

이제 Await는 점에서 이상한 정신 분열증을 가지고 할 수 있습니다 중 하나를 내려면 Option[Try[B]], 방법 ready, 또는 result를 사용 풀린 B를 사용. 문제는 외부의 미래를 완료하기 전에 Failure의 경우 잠금을 해제해야하므로 Await.ready을 고수해야합니다.

이 못생긴 가져옵니다

val fut = producer 
val prod = Await.ready(fut, Duration.Inf).value.get 
if (prod.isFailure) sync.synchronized { locked = false } 
prod.get 

이 그 추한 될 수 있을까? 이것을하기위한 더 좋은 방법이 있어야합니다.


그래서 반복하십시오 Future[B] 내에서 또한 B으로 완료 일부 피어 미래를 실행하고 피어의 결과를 반환하지만, 실패의 경우, 주요 미래를 완료하기 전에 잠금을 청소하십시오.

답변

0

여기서 recover(With)을 사용하여 Await을 피하면됩니다. 그래도 예외가 다시 발생해야하기 때문에 어색해 보인다.

import concurrent._ 

trait Cache[A, B] { 
    class Entry(var locked: Boolean = true) 

    private var map = Map.empty[A, Entry] 
    private val sync = new AnyRef 

    implicit def exec: ExecutionContext 

    def readEntry(key: A): Option[B] 

    def acquire(key: A, producer: => Future[B]): Future[B] = sync.synchronized { 
    map.get(key) match { 
     case Some(e) => 
     if (e.locked) throw new IllegalStateException() 
     e.locked  = true 
     val existing = future { readEntry(key).get } 
     val refresh = existing.recoverWith { 
      case _: NoSuchElementException => producer 
     } 
     refresh.recover { 
      case t => sync.synchronized(e.locked = false); throw t 
     } 

     case _ => producer.map { value => 
     sync.synchronized { 
      map += key -> new Entry() 
     } 
     value 
     } 
    } 
    } 
} 

제안 사항이 있으면 별도의 답변으로 게시하시기 바랍니다.

+0

나는 인수 사실을 조금 혼란스럽게 생각합니다. 잠금은 항목에 연결되어 있으므로 반환되지 않습니다. 어떻게 잠금이 해제됩니까? 생략 된 release() 메소드가 있습니까? –

+0

@ArneClaassen - 예, 죄송합니다. 'release' 메소드가 있습니다. 그것은 질문에 직접적으로 관련되지 않기 때문에 묘사되지 않았습니다. –

0

구현에 몇 가지 변경 사항을 적용했습니다.

먼저 시계 소비자가 실패한 미래라고 가정하는 것이 안전해야하며 처음으로 throw을 잠갔습니다. 선물 소비자가 Future.failed에 잠겨 있습니다.

다음으로 을 Option[B]에서 readEntry으로 전화하는 대신 장래의 결과로 반환합니다. 그렇다면 flatMap 결과는 None의 경우 producer에 의해 생성 된 미래를 대체 할 수 있습니다 (지도는 Future [Future [B]]가됩니다). Some의 경우 flatMap에 미래가 반환되어야하기 때문에 값에서 Future.successful을 반환합니다.우리는이 항목의 잠금을 해제하기 위해 미래에 부작용을 전파 만 체인 싶은 Future 실패를 원하기 때문에

마지막으로, 나는 andThenrecoverthrow를 교체했습니다.

trait Cache[A, B] { 
    class Entry(var locked: Boolean = true) 

    private var map = Map.empty[A, Entry] 
    private val sync = new AnyRef 

    def readEntry(key: A): Option[B] = ??? 

    def acquire(key: A, producer: => Future[B]): Future[B] = sync.synchronized { 
    map.get(key) match { 
     case Some(e) => 
     if (e.locked) 
      Future.failed(new IllegalStateException()) 
     else { 
     e.locked = true 
     future { readEntry(key)}.flatMap { 
      case None => producer.andThen { 
      case Failure(_) => sync.synchronized(e.locked = false) 
      } 
      case Some(value) => Future.successful(value) 
     } 
     } 

     case _ => producer.map { value => 
     sync.synchronized { 
      map += key -> new Entry() 
     } 
     value 
     } 
    } 
    } 
} 
관련 문제