스칼라 액터 : 수신 대 반응


110

먼저 Java 경험이 많지만 최근에야 함수형 언어에 관심이 생겼다고 말씀 드리겠습니다. 최근에 저는 아주 좋은 언어처럼 보이는 Scala를보기 시작했습니다.

그러나 저는 Programming in Scala 에서 Scala의 Actor 프레임 워크에 대해 읽고 있는데, 제가 이해하지 못하는 한 가지가 있습니다. 30.4 장에서는 react대신 receive사용하면 스레드를 재사용 할 수 있으며 이는 JVM에서 스레드가 비싸기 때문에 성능에 좋습니다.

react대신 을 호출 하는 것을 기억하는 한 receive원하는만큼 액터를 시작할 수 있다는 뜻 입니까? Scala를 발견하기 전에 저는 Erlang을 가지고 놀았으며 Programming Erlang 의 저자 는 땀을 흘리지 않고 200,000 개 이상의 프로세스를 생성하는 것을 자랑합니다. 나는 Java 스레드로 그렇게하는 것을 싫어합니다. Erlang (및 Java)과 비교하여 Scala에서 어떤 종류의 제한을보고 있습니까?

또한이 스레드 재사용은 Scala에서 어떻게 작동합니까? 간단하게 스레드가 하나만 있다고 가정 해 보겠습니다. 내가 시작한 모든 액터가이 스레드에서 순차적으로 실행됩니까, 아니면 일종의 작업 전환이 발생합니까? 예를 들어 서로에게 메시지를 핑퐁하는 두 액터를 시작하면 동일한 스레드에서 시작되면 교착 상태가 발생할 위험이 있습니까?

Programming in Scala 에 따르면 사용할 액터를 작성하는 react것은 receive. react반환되지 않기 때문에 이것은 그럴듯하게 들립니다 . 그러나이 책은 계속해서를 react사용하여 루프 안에 넣는 방법을 보여줍니다 Actor.loop. 결과적으로

loop {
    react {
        ...
    }
}

나에게 꽤 비슷해 보인다

while (true) {
    receive {
        ...
    }
}

이 책의 앞부분에서 사용되었습니다. 그럼에도 불구하고이 책은 "실제로 프로그램은 최소한 몇 개의 receive' 가 필요하다 '고 말합니다. 그래서 내가 여기서 무엇을 놓치고 있습니까? 무엇을 할 수 receive그렇게 react반환 외에, 수 없습니다를? 그리고 내가 왜 신경 쓰나요?

마지막으로, 내가 이해하지 못하는 것의 핵심에 도달했습니다.이 책에서는 using react이 스레드를 재사용하기 위해 호출 스택을 버릴 수 있는 방법을 계속 언급 하고 있습니다. 어떻게 작동합니까? 호출 스택을 버려야하는 이유는 무엇입니까? 그리고 함수가 예외 ( react) 를 던져 종료 할 때 호출 스택을 버릴 수있는 이유는 receive무엇입니까?

저는 Programming in Scala 가 여기에서 몇 가지 주요 문제를 다루고 있다는 인상을 받았습니다. 그렇지 않으면 정말 훌륭한 책이기 때문입니다.


답변:


78

첫째, 대기중인 각 액터 receive가 스레드를 차지하고 있습니다. 아무것도 수신하지 않으면 해당 스레드는 아무것도 수행하지 않습니다. on 액터 react는 무언가를받을 때까지 스레드를 차지하지 않습니다. 무언가를 받으면 스레드가 할당되고 초기화됩니다.

이제 초기화 부분이 중요합니다. 수신 스레드는 무언가를 반환 할 것으로 예상되지만 반응하는 스레드는 그렇지 않습니다. 따라서 마지막 끝에있는 이전 스택 상태 react는 완전히 버릴 수 있으며 완전히 버릴 수 있습니다. 스택 상태를 저장하거나 복원 할 필요가 없어 스레드를 더 빠르게 시작할 수 있습니다.

하나 또는 다른 것을 원하는 다양한 성능 이유가 있습니다. 아시다시피 Java에 스레드가 너무 많은 것은 좋은 생각이 아닙니다. 반면에, 스레드에 액터를 첨부해야하기 때문에 메시지보다 메시지에 react더 빠릅니다 . 따라서 많은 메시지를 수신하지만 거의 사용하지 않는 액터가있는 경우 추가 지연으로 인해 목적에 비해 너무 느려질 수 있습니다.receivereactreact


21

대답은 "예"입니다. 액터가 코드의 어떤 것도 차단하지 않고를 사용 하는 경우 단일 스레드 내에서 "동시" 프로그램을 react실행할 수 있습니다 (확인하려면 시스템 속성 을 설정해보십시오 ).actors.maxPoolSize

호출 스택을 버려야 하는 더 분명한 이유 중 하나는 그렇지 않으면 loop메서드가 StackOverflowError. 그대로 프레임 워크는 a react를 던짐으로써 a 를 영리하게 끝냅니다. 이 SuspendActorException코드는 루핑 코드에 의해 잡히고 메서드 react를 통해 다시 실행됩니다 andThen.

mkBody방법을 살펴본 Actor다음 seq루프가 자체적으로 일정을 재조정하는 방법을 확인하십시오.


20

"스택을 버리는 것"이라는 말은 한동안 저를 혼란스럽게했고 저는 지금 그것을 얻었으며 이것이 지금 제 이해입니다. "수신"의 경우 메시지에 대한 전용 스레드 차단 (모니터에서 object.wait () 사용)이 있으며 이는 전체 스레드 스택을 사용할 수 있으며 수신시 "대기"지점에서 계속할 준비가되었음을 의미합니다. 메시지. 예를 들어 다음 코드가있는 경우

  def a = 10;
  while (! done)  {
     receive {
        case msg =>  println("MESSAGE RECEIVED: " + msg)
     }
     println("after receive and printing a " + a)
  }

스레드는 메시지가 수신 될 때까지 수신 호출에서 대기 한 다음 계속해서 "10을 수신하고 인쇄 한 후"메시지를 인쇄하고 스레드가 차단되기 전에 스택 프레임에있는 "10"값을 인쇄합니다.

react의 경우 이러한 전용 스레드가없는 경우 react 메서드의 전체 메서드 본문이 클로저로 캡처되어 메시지를 수신하는 해당 액터의 임의 스레드에 의해 실행됩니다. 즉, 클로저로만 캡처 할 수있는 명령문 만 실행되고 여기서 "Nothing"반환 유형이 실행됩니다. 다음 코드를 고려하십시오.

  def a = 10;
  while (! done)  {
     react {
        case msg =>  println("MESSAGE RECEIVED: " + msg)
     }
     println("after react and printing a " + a) 
  }

react에 void의 반환 유형이있는 경우 "react"호출 (예에서 "after react and printing a 10"메시지를 인쇄하는 println 문) 이후에 문을 갖는 것이 합법적이지만 실제로는 "react"메서드의 본문 만 캡처되고 나중에 실행되도록 순서가 지정되므로 (메시지 도착시) 실행되지 않습니다. react의 컨트랙트가 "Nothing"의 반환 유형을 가지기 때문에 react 뒤에 어떤 문도있을 수 없으며 스택을 유지할 이유가 없습니다. 위의 예에서 변수 "a"는 반응 호출이 전혀 실행되지 않은 이후의 명령문으로 유지 될 필요가 없습니다. react 본문에 필요한 모든 변수는 이미 클로저로 캡처되어 있으므로 잘 실행할 수 있습니다.

자바 액터 프레임 워크 Kilim은 실제로 메시지를받는 반응에서 펼쳐지는 스택을 저장하여 스택 유지 관리를 수행합니다.


감사합니다. 매우 유익했습니다. 그러나 당신은 +a코드 스 니펫에서 의미하지 않았 +10습니까?
jqno

좋은 대답입니다. 나는 그것도 이해하지 못한다.
santiagobasulto

8

여기에 그것을 가지고 :

제어 반전이없는 이벤트 기반 프로그래밍

이 문서는 액터 용 스칼라 API에서 링크되어 액터 구현을위한 이론적 프레임 워크를 제공합니다. 여기에는 반응이 반환되지 않는 이유가 포함됩니다.


그리고 두 번째 논문. Bad spam control ... :( [스레드 및 이벤트를 통합하는 행위자] [2] [2] : lamp.epfl.ch/~phaller/doc/haller07coord.pdf "스레드 및 이벤트를 통합하는 행위자"
Hexren

0

나는 scala / akka에 대한 주요 작업을 한 적이 없지만 배우가 예약되는 방식에 매우 큰 차이가 있음을 이해합니다. Akka는 액터의 실행을 타임 슬라이싱하는 스마트 스레드 풀일뿐입니다 ... 매번 슬라이스는 명령어 당 할 수있는 Erlang과는 달리 액터에 의해 완료까지 하나의 메시지 실행이 될 것입니까?!

이것은 현재 스레드가 동일한 액터에 대해 다른 메시지를 계속 실행하기 위해 "수신"이 현재 스레드에 참여하는 스케줄링을 위해 다른 액터를 고려하도록 현재 스레드가 더 낫다고 생각하게합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.