scala.concurrent.Promise의 사용 사례는 무엇입니까?


93

저는 SIP-14를 읽고 있으며 개념 Future이 완벽하고 이해하기 쉽습니다. 그러나 다음에 대해 두 가지 질문이 있습니다 Promise.

  1. SIP는 말한다 Depending on the implementation, it may be the case that p.future == p. 어떻게 이럴 수있어? 인가 FuturePromise서로 다른 두 가지 종류가 있지?

  2. 우리는 언제 사용해야 Promise합니까? 예제 producer and consumer코드 :

    import scala.concurrent.{ future, promise }
    val p = promise[T]
    val f = p.future
    
    val producer = future {
        val r = produceSomething()
        p success r
        continueDoingSomethingUnrelated()
    }
    val consumer = future {
        startDoingSomething()
        f onSuccess {
            case r => doSomethingWithResult()
        }
    }

읽기 쉽지만 그렇게 써야하나요? 나는 다음과 같이 미래와 약속없이 그것을 구현하려고했습니다.

val f = future {
   produceSomething()
}

val producer = future {
   continueDoingSomethingUnrelated()
}

startDoingSomething()

val consumer = future {
  f onSuccess {
    case r => doSomethingWithResult()
  }
}

이것과 주어진 예의 차이점은 무엇이며 약속이 필요한 이유는 무엇입니까?


첫 번째 예제에서 continueDoingSomethingUnrelated ()는 동일한 스레드에서 productionSomething () 이후에 평가됩니다.
senia

1
질문 # 1에 답하기 위해 yes Future, Promise두 가지 유형이 있지만 github.com/scala/scala/blob/master/src/library/scala/concurrent/… 에서 볼 수 있듯이이 특정 Promise구현도 확장 Future됩니다.
Dylan

답변:


118

약속과 미래는 상호 보완적인 개념입니다. 미래는 언젠가 검색 될 가치이며 그 사건이 발생했을 때 그것을 가지고 무언가를 할 수 있습니다. 따라서 이는 계산의 읽기 또는 출력 끝점이며 값을 검색하는 것입니다.

Promise는 유사하게 계산의 작성 측면입니다. 당신은 계산의 결과를 넣을 장소 인 promise를 만들고 그 약속으로부터 약속에 넣은 결과를 읽는 데 사용될 미래를 얻습니다. 실패 또는 성공으로 Promise를 완료하면 연결된 Future에 연결된 모든 동작이 트리거됩니다.

첫 번째 질문과 관련하여 우리가 가진 약속 p에 대해 어떻게 될 수 있습니까 p.future == p? 이것은 단일 항목 버퍼 (처음에 비어있는 컨테이너)와 같이 상상할 수 있으며, 나중에 내용이 영원히 내용이 될 하나의 값을 저장할 수 있습니다. 이제 여러분의 관점에 따라 이것은 약속이자 미래입니다. 버퍼에 값을 쓰려는 사람에게는 약속입니다. 그 값이 버퍼에 들어가기를 기다리는 사람에게는 미래입니다.

특히 Scala 동시 API의 경우 여기 에서 Promise 특성을 살펴보면 Promise 컴패니언 객체의 메서드가 구현되는 방식을 볼 수 있습니다.

object Promise {

  /** Creates a promise object which can be completed with a value.
   *  
   *  @tparam T       the type of the value in the promise
   *  @return         the newly created `Promise` object
   */
  def apply[T](): Promise[T] = new impl.Promise.DefaultPromise[T]()

  /** Creates an already completed Promise with the specified exception.
   *  
   *  @tparam T       the type of the value in the promise
   *  @return         the newly created `Promise` object
   */
  def failed[T](exception: Throwable): Promise[T] = new impl.Promise.KeptPromise[T](Failure(exception))

  /** Creates an already completed Promise with the specified result.
   *  
   *  @tparam T       the type of the value in the promise
   *  @return         the newly created `Promise` object
   */
  def successful[T](result: T): Promise[T] = new impl.Promise.KeptPromise[T](Success(result))

}

이제 이러한 promise 구현, DefaultPromise 및 KeptPromise는 여기 에서 찾을 수 있습니다 . 둘 다 동일한 이름을 가진 기본 작은 특성을 확장하지만 다른 패키지에 있습니다.

private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] {
  def future: this.type = this
}

그래서 당신은 그들이 의미하는 바를 볼 수 있습니다 p.future == p.

DefaultPromise는 위에서 언급 한 KeptPromise버퍼이고, 생성 된 값을 가진 버퍼입니다.

귀하의 예와 관련하여 거기에서 사용하는 미래 블록은 실제로 장면 뒤에서 약속을 만듭니다. 의 정의에서 살펴 보자 future여기 :

def future[T](body: =>T)(implicit execctx: ExecutionContext): Future[T] = Future[T](body)

일련의 메소드를 따르면 impl.Future로 끝납니다 .

private[concurrent] object Future {
  class PromiseCompletingRunnable[T](body: => T) extends Runnable {
    val promise = new Promise.DefaultPromise[T]()

    override def run() = {
      promise complete {
        try Success(body) catch { case NonFatal(e) => Failure(e) }
      }
    }
  }

  def apply[T](body: =>T)(implicit executor: ExecutionContext): scala.concurrent.Future[T] = {
    val runnable = new PromiseCompletingRunnable(body)
    executor.execute(runnable)
    runnable.promise.future
  }
}

보시다시피 생산자 블록에서 얻은 결과가 약속에 부어집니다.

나중에 편집 :

실제 사용과 관련하여 : 대부분의 경우 약속을 직접 처리하지 않습니다. 비동기 계산을 수행하는 라이브러리를 사용하는 경우 라이브러리의 메서드에서 반환 된 Future로 작업 할 것입니다. 이 경우 약속은 라이브러리에 의해 생성됩니다. 이러한 메서드가 수행하는 작업의 읽기 끝으로 작업하고 있습니다.

그러나 자체 비동기 API를 구현해야하는 경우 작업을 시작해야합니다. 예를 들어 Netty 위에 비동기 HTTP 클라이언트를 구현해야한다고 가정합니다. 그러면 코드가 다음과 같이 보일 것입니다.

    def makeHTTPCall(request: Request): Future[Response] = {
        val p = Promise[Response]
        registerOnCompleteCallback(buffer => {
            val response = makeResponse(buffer)
            p success response
        })
        p.future
    }

3
@xiefei Promises 의 사용 사례 는 구현 코드에 있어야합니다. Future클라이언트 코드에 노출 할 수있는 읽기 전용 기능입니다. 또한 Future.future{...}구문이 때때로 번거로울 수 있습니다.
Dylan

11
다음과 같이 볼 수 있습니다. 약속 없이는 미래를 가질 수 없습니다. 처음에 완료되는 약속이 없으면 Future는 값을 반환 할 수 없습니다. 약속은 선택 사항이 아니며 미래의 필수 작성 측면입니다. 선물에 반환 값을 제공 할 사람이 없기 때문에 선물로만 작업 할 수 없습니다.
Marius Danila

4
나는 당신이 실제 세계에서 의미하는 바를 알고 있다고 생각합니다. 예를 들어 제 응답을 업데이트했습니다.
Marius Danila

2
@Marius는 : 주어진 실제 예제를 고려, 어떤 makeHTTPCall는 다음과 같이 구현 된 경우 : def makeHTTPCall(request: Request): Future[Response] = { Future { registerOnCompleteCallback(buffer => { val response = makeResponse(buffer) response }) } }
puneetk

1
@puneetk 그러면 완료 후 바로 완료되는 미래가 있습니다 registerOnCompleteCallback(). 또한 반환하지 않습니다 Future[Response]. Future[registerOnCompleteCallback() return type]대신 반환 됩니다.
Evgeny Veretennikov
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.