2012-06-21 4 views
12

Akka docs에서 액세사리의 변수를 닫는 것이 위험합니다. 주의 깊게 익명 Actor 클래스 내에서 둘러싸는 배우의 메소드를 호출하지 마십시오 포함하는 배우의 참조를 통해 폐쇄 즉를 방지하기 위해 필요한이 경우Akka 배우, 선물 및 종결

경고

. 그러면 은 액터 캡슐화를 깨고 동기화 비트와 경합 조건을 야기 할 수 있습니다. 다른 액터의 코드는 동봉 액터와 동시에 으로 예약 될 것이기 때문입니다.

저는 두 명의 액터가 있습니다. 그 중 하나는 두 번째 액터에서 뭔가를 요청하고 그 결과로 무언가를합니다. 아래 예제에서 나는 함께 배우를 넣었습니다 누산기NumberGenerator의 숫자를 검색하여 합계를보고하면서 합계를보고합니다. 다른 함수 ( B VS) 개의 수신이 예와 같이

이 적어도 두 가지 방법으로 수행 될 수있다. 두 가지의 차이점은 카운터 변수를 닫지 않습니다. B는 카운터 위에 닫히고 합 않는 미래을 생성하면서 그 대신 정수을 대기하고 요약한다. 이것은 내가 어떻게 제대로 작동 하는지를 이해한다면, onSuccess를 처리하기 위해 만들어진 익명의 배우 안에서 발생합니다.

import com.esotericsoftware.minlog.Log 

import akka.actor.{Actor, Props} 
import akka.pattern.{ask, pipe} 
import akka.util.Timeout 
import akka.util.duration._ 

case object Start 
case object Request 


object ActorTest { 
    var wake = 0 

    val accRef = Main.actorSystem.actorOf(Props[Accumulator], name = "accumulator") 
    val genRef = Main.actorSystem.actorOf(Props[NumberGenerator], name = "generator") 

    Log.info("ActorTest", "Starting !") 

    accRef ! Start 
} 

class Accumulator extends Actor { 
    var counter = 0 

    implicit val timeout = Timeout(5 seconds) 

    // A: WITHOUT CLOSURE 
    def receive = { 
    case Start => ask(ActorTest.genRef, Request).mapTo[Int] pipeTo self 
    case x: Int => counter += x; Log.info("Accumulator", "counter = " + counter); self ! Start 
    } 
    // B: WITH CLOSURE 
    def receive = { 
    case Start => ask(ActorTest.genRef, Request).mapTo[Int] onSuccess { 
     case x: Int => counter += x; Log.info("Accumulator", "counter = " + counter); self ! Start 
    } 
    } 
} 

class NumberGenerator extends Actor { 
    val rand = new java.util.Random() 

    def receive = { 
    case Request => sender ! rand.nextInt(11)-5 
    } 
} 

이 경우 클로저를 사용하는 것은 완전히 위험합니까? 물론 Int 대신 AtomicInteger를 사용하거나 netty을 사용하는 일부 네트워킹 시나리오에서는 threadsafe 채널에서 쓰기 작업을 수행 할 수 있지만 여기에는 내 요점이 없습니다. 말도 안되는 요청의 위험

는 : 대신 익명의 중간 배우의 배우를 실행 경우를 정의없이 할 수있는 미래의는 onSuccess위한 방법이 수신 기능인가?

편집

더 명확하게 말하면, 내 질문은 : 주어진 배우와 같은 스레드에서 실행하는 선물의 시리즈를 강제 할 수있는 방법이 있습니까?

답변

5

문제는 onSuccess이 배우의 receive가에서 실행하는 것입니다 스레드가 아닌 다른 스레드에서 실행하는 것입니다 것입니다. 당신은 pipeTo 방법을 사용하거나 Agent를 사용할 수 있습니다. counterAtomicInteger으로 설정하면 문제가 해결되지만 너무 깨끗하지는 않습니다. 즉, 액터 모델이 손상됩니다.

class Accumulator extends Actor { 
    private[this] var counter = 0 

    def receive = { 
    case Start => ActorTest.genRef ! Request 
    case x: Int => { 
     counter += x 
     Log.info("Accumulator", "counter = " + counter) 
     self ! Start 
    } 
    } 
} 

이 솔루션은 완전히 비동기, 당신은 시간 제한이 필요하지 않습니다 :

+1

+1 상담원의 사용을 제안합니다. – gsimard

5

같은 디자인을 구현하는 가장 쉬운 방법은 의미 "불이 앤 잊지"사용하는 것입니다.

+0

예, 선물을 사용하여 포기하는 경우에 작동합니다. Chaining Futures는 _pipeTo self_로 끝나고 fire-and-forget 의미론으로 더 이상 가능하지 않은 경우 예제에서 쉽게 사용할 수 있습니다. 대신 Accumulator의 receive 함수에서 N 개의 중간 메시지를 정의하여 코드가이 Actor 스레드 내에서 실행되도록 보장해야합니다. 이번에는 다시 묻습니다.이 일련의 Future를 주어진 Actor와 동일한 스레드에서 실행하도록하는 방법이 있습니까? – gsimard

+0

왜 'Accumulator' Actor가 항상 같은 스레드에서 실행되도록 보장해야합니까? 그것은 배우 모델 철학에 반대하는 것 같습니다. 미래에 대해서도 마찬가지입니다. 공연을 극대화하기 위해 스레드 풀에 파견되어야합니다. 그들은 모두 체인의 동일한 스레드에서 실행되는 경우 일반 순차 프로그램이 있으며 더 이상 선물이 필요하지 않습니다 ... – paradigmatic

+0

실제로 동일한 스레드에서 실행되는지 여부는 중요하지 않습니다. 요구 사항은 다음과 같습니다. 단일 액터의 메시지가 처리되는 것처럼 순차적으로 실행되어야합니다. 이것은 액터 모델에 반대하지 않습니다. 액터 모델입니다. – gsimard

관련 문제