programing

Scala의 액터는 Go의 코 루틴과 비슷합니까?

nasanasas 2020. 10. 28. 08:09
반응형

Scala의 액터는 Go의 코 루틴과 비슷합니까?


고 루틴을 사용하는 Go 라이브러리를 포팅하려는 경우받은 편지함 / akka 프레임 워크가 본질적으로 코 루틴과 유사하기 때문에 Scala가 좋은 선택일까요?


아니요, 그렇지 않습니다. 고 루틴은 1978 년 Tony Hoare가 지정한대로 순차적 프로세스 통신 이론을 기반으로합니다. 아이디어는 서로 독립적으로 작동하지만 하나의 프로세스 / 스레드가 데이터를 저장하는 "채널"을 공유하는 두 개의 프로세스 또는 스레드가있을 수 있다는 것입니다. 다른 프로세스 / 스레드가 소비합니다. 가장 눈에 띄는 구현은 Go의 채널과 Clojure의 구현 core.async이지만 현재는 현재 런타임으로 제한되며 동일한 물리적 상자의 두 런타임 간에도 배포 할 수 없습니다.

CSP는 코드에 교착 상태가 있음을 입증하기위한 정적이고 공식적인 프로세스 대수를 포함하도록 발전했습니다. 이것은 정말 멋진 기능이지만 Goroutines도 core.async현재 지원 하지도 않습니다 . 만약 그렇다면, 교착 상태가 가능한지 여부를 코드를 실행하기 전에 아는 것이 매우 좋을 것입니다. 그러나 CSP는 의미있는 방식으로 내결함성을 지원하지 않으므로 개발자는 채널의 양쪽에서 발생할 수있는 오류를 처리하는 방법을 파악해야하며 이러한 논리는 결국 애플리케이션 전체에 흩어져 있습니다.

1973 년 Carl Hewitt가 지정한대로 행위자는 자체 사서함을 가진 엔티티를 포함합니다. 본질적으로 비동기 적이며 런타임과 머신에 걸친 위치 투명성을 가지고 있습니다. 액터의 참조 (Akka) 또는 PID (Erlang)가있는 경우 메시지를 보낼 수 있습니다. 이것은 또한 어떤 사람들이 Actor 기반 구현에서 결함을 발견하는 곳입니다. 메시지를 보내기 위해 다른 행위자에 대한 참조가 있어야하므로 송신자와 수신자를 직접 결합해야합니다. CSP 모델에서 채널은 공유되며 여러 생산자와 소비자가 공유 할 수 있습니다. 제 경험상 이것은별로 문제가되지 않았습니다. 나는 내 코드에 메시지를 보내는 방법에 대한 구현 세부 사항이 흩어져 있지 않다는 것을 의미하는 프록시 참조의 아이디어가 마음에 듭니다. 나는 단지 하나를 보내고 액터가 어디에 있든 그것을받습니다.

액터에는 내결함성이라는 또 다른 매우 멋진 기능이 있습니다. Erlang에서 고안된 OTP 사양에 따라 행위자를 감독 계층 구조로 구성함으로써 애플리케이션에 장애 도메인을 구축 할 수 있습니다. 값 클래스 / DTO / 당신이 부르고 싶은 것과 마찬가지로 실패, 처리 방법 및 계층 구조 수준을 모델링 할 수 있습니다. 이것은 CSP 내부에 오류 처리 기능이 거의 없기 때문에 매우 강력합니다.

액터는 또한 동시성 패러다임으로, 액터 기반 시스템을 구축하는 개발자가 예를 들어 액터를 리스너로 등록하여 실수로 도입하지 않는 한 액터 내부에 가변 상태를 가질 수 있고 상태에 대한 다중 스레드 액세스를 보장 할 수 없습니다. 콜백을 위해 또는 Futures를 통해 액터 내부에서 비 동기화됩니다.

뻔뻔한 플러그-Akka 팀장 인 Roland Kuhn과 함께 Reactive Design Patterns라는 새 책을 쓰고이 모든 것을 논의하고 있습니다. 녹색 스레드, CSP, 이벤트 루프, 반복, 반응 확장, 액터, 선물 / 약속 등. 다음 달 초까지 Manning에 대한 MEAP를 볼 수있을 것으로 예상됩니다.

행운을 빕니다!


여기에 두 가지 질문이 있습니다.

  • Scala는 이식하기에 좋은 선택 goroutines입니까?

Scala는 범용 언어이므로 "포트 고 루틴"을 선택할 수있는 다른 언어보다 나쁘거나 낫지 않기 때문에 이것은 쉬운 질문입니다.

물론 Scala가 언어로서 더 좋거나 나쁘게 된 이유에 대한 많은 의견 이 있습니다 (예 : 여기 에 내 것이 있습니다). 그러나 이것은 단지 의견 일 뿐이며 그들이 당신을 멈추게하지 마십시오. Scala는 범용이기 때문에 "거의"로 귀결됩니다. 언어 X에서 할 수있는 모든 것, Scala에서 할 수있는 것입니다. 너무 광범위하게 들리면 .. Java에서 계속되는 것은 어떻 습니까? :)

  • Scala 액터는 다음과 비슷 goroutines합니까?

유일한 유사점 (nitpicking 제외)은 둘 다 동시성 및 메시지 전달과 관련이 있다는 것입니다. 그러나 그것이 유사성이 끝나는 곳입니다.

Jamie의 답변이 Scala 액터에 대한 좋은 개요를 제공했기 때문에 Goroutines / core.async에 더 중점을 둘 것입니다.

배우는 "걱정없는 배포"를 도와줍니다


는 "걱정 무료"조각은 일반적으로 다음과 같은 용어와 연관되는 경우 : fault tolerance, resiliency, availability, 등

배우가 작동하는 방식에 대해 자세히 설명하지 않고 두 가지 간단한 용어로 배우는 다음과 관련이 있습니다.

  • 지역성 : 각 행위자는 다른 행위자가 메시지를 보내는 데 사용할 수있는 주소 / 참조를 가지고 있습니다.
  • Behavior : 메시지가 액터에게 도착했을 때 적용 / 호출되는 함수

각 프로세스가 참조와 메시지가 도착할 때 호출되는 함수가있는 "말하는 프로세스"를 생각하십시오.

물론 훨씬 더 많은 것이 있지만 (예 : Erlang OTP 또는 akka docs 확인 ) 위의 두 가지가 좋은 시작입니다.

배우들에게 흥미로운 부분은 .. 구현입니다. 현재 두 가지 큰 것은 Erlang OTP와 Scala AKKA입니다. 둘 다 같은 문제를 해결하는 것을 목표로하지만 몇 가지 차이점이 있습니다. 몇 가지를 살펴 보겠습니다.

  • 나는 의도적으로 "참조 투명성", "멱 등성"등과 같은 용어를 사용하지 않습니다. 그들은 혼란을 야기하는 것 외에 소용이 없으므로 불변성 [ can't change that개념]에 대해 이야기합시다 . 언어로서의 Erlang은 독단적이며 강력한 불변성에 의지하는 반면 Scala에서는 메시지가 수신 될 때 상태를 변경 / 변형하는 행위자를 만들기가 너무 쉽습니다. 권장하지는 않지만 Scala의 변경 가능성은 바로 앞에 있으며 사람들 그것을 사용합니다.

  • Joe Armstrong이 언급 한 또 다른 흥미로운 점은 Scala / AKKA가 실제로 "분산"을 염두에두고 설계되지 않은 JVM에 의해 제한된다는 사실입니다. 반면 Erlang VM은 그랬습니다. 이는 프로세스 격리, 프로세스 별, 전체 VM 가비지 수집, 클래스 로딩, 프로세스 스케줄링 등과 같은 많은 것들과 관련이 있습니다.

위의 요점은 하나가 다른 것보다 낫다고 말하는 것이 아니라 개념으로서의 배우 모델의 순도가 구현에 달려 있음을 보여주는 것입니다.

이제 고 루틴으로 ..

고 루틴은 동시성에 대해 순차적으로 추론하는 데 도움이됩니다.


이미 언급 한 다른 답변과 같이 고 루틴 은 "동시 시스템에서 상호 작용 패턴을 설명하는 공식 언어"인 Communicating Sequential Processes에 뿌리를두고 있으며 정의상 거의 모든 것을 의미 할 수 있습니다. :)

루틴 보다 내부를 더 잘 알고 있기 때문에 core.async를 기반으로 예제를 제공 할 것입니다. 그러나 고 core.async루틴 / CSP 모델 이후에 구축되었으므로 개념적으로 너무 많은 차이가 있어서는 안됩니다.

core.async / Goroutine의 주요 동시성 프리미티브는 channel. channela를 "바위 위의 대기열" 이라고 생각하십시오 . 이 채널은 메시지를 "전달"하는 데 사용됩니다. "게임에 참여"하려는 모든 프로세스는 a에 대한 참조를 생성하거나 가져오고 여기에 channel메시지를 넣고 /받습니다 (예 : 보내기 / 받기).

24 시간 무료 주차

채널에서 수행되는 대부분의 작업은 일반적으로 " Goroutine "또는 " go block " 내부에서 발생 합니다.이 작업은 본문을 가져 와서 모든 채널 작업을 검사합니다. 본문을 상태 시스템으로 전환합니다. 모든 차단 작업에 도달하면 상태 머신이 '파킹'되고 실제 제어 스레드가 해제됩니다.이 접근 방식은 C # 비동기에서 사용되는 것과 유사합니다. 차단 작업이 완료되면 코드가 다시 시작됩니다 (스레드 풀 스레드 또는 JS VM의 유일한 스레드) "( 소스 ).

시각적으로 전달하는 것이 훨씬 쉽습니다. 차단 IO 실행은 다음과 같습니다.

IO 차단

You can see that threads mostly spend time waiting for work. Here is the same work but done via "Goroutine"/"go block" approach:

core.async

Here 2 threads did all the work, that 4 threads did in a blocking approach, while taking the same amount of time.

The kicker in above description is: "threads are parked" when they have no work, which means, their state gets "offloaded" to a state machine, and the actual live JVM thread is free to do other work (source for a great visual)

note: in core.async, channel can be used outside of "go block"s, which will be backed by a JVM thread without parking ability: e.g. if it blocks, it blocks the real thread.

Power of a Go Channel

Another huge thing in "Goroutines"/"go blocks" is operations that can be performed on a channel. For example, a timeout channel can be created, which will close in X milliseconds. Or select/alt! function that, when used in conjunction with many channels, works like a "are you ready" polling mechanism across different channels. Think about it as a socket selector in non blocking IO. Here is an example of using timeout channel and alt! together:

(defn race [q]
  (searching [:.yahoo :.google :.bing])
  (let [t (timeout timeout-ms)
        start (now)]
    (go
      (alt! 
        (GET (str "/yahoo?q=" q))  ([v] (winner :.yahoo v (took start)))
        (GET (str "/bing?q=" q))   ([v] (winner :.bing v (took start)))
        (GET (str "/google?q=" q)) ([v] (winner :.google v (took start)))
        t                          ([v] (show-timeout timeout-ms))))))

This code snippet is taken from wracer, where it sends the same request to all three: Yahoo, Bing and Google, and returns a result from the fastest one, or times out (returns a timeout message) if none returned within a given time. Clojure may not be your first language, but you can't disagree on how sequential this implementation of concurrency looks and feels.

You can also merge/fan-in/fan-out data from/to many channels, map/reduce/filter/... channels data and more. Channels are also first class citizens: you can pass a channel to a channel..

Go UI Go!

Since core.async "go blocks" has this ability to "park" execution state, and have a very sequential "look and feel" when dealing with concurrency, how about JavaScript? There is no concurrency in JavaScript, since there is only one thread, right? And the way concurrency is mimicked is via 1024 callbacks.

But it does not have to be this way. The above example from wracer is in fact written in ClojureScript that compiles down to JavaScript. Yes, it will work on the server with many threads and/or in a browser: the code can stay the same.

Goroutines vs. core.async

Again, a couple of implementation differences [there are more] to underline the fact that theoretical concept is not exactly one to one in practice:

  • In Go, a channel is typed, in core.async it is not: e.g. in core.async you can put messages of any type on the same channel.
  • In Go, you can put mutable things on a channel. It is not recommended, but you can. In core.async, by Clojure design, all data structures are immutable, hence data inside channels feels a lot safer for its wellbeing.

So what's the verdict?


I hope the above shed some light on differences between the actor model and CSP.

Not to cause a flame war, but to give you yet another perspective of let's say Rich Hickey:

"I remain unenthusiastic about actors. They still couple the producer with the consumer. Yes, one can emulate or implement certain kinds of queues with actors (and, notably, people often do), but since any actor mechanism already incorporates a queue, it seems evident that queues are more primitive. It should be noted that Clojure's mechanisms for concurrent use of state remain viable, and channels are oriented towards the flow aspects of a system."(source)

However, in practice, Whatsapp is based on Erlang OTP, and it seemed to sell pretty well.

Another interesting quote is from Rob Pike:

"Buffered sends are not confirmed to the sender and can take arbitrarily long. Buffered channels and goroutines are very close to the actor model.

The real difference between the actor model and Go is that channels are first-class citizens. Also important: they are indirect, like file descriptors rather than file names, permitting styles of concurrency that are not as easily expressed in the actor model. There are also cases in which the reverse is true; I am not making a value judgement. In theory the models are equivalent."(source)


Moving some of my comments to an answer. It was getting too long :D (Not to take away from jamie and tolitius's posts; they're both very useful answers.)

It isn't quite true that you could do the exact same things that you do with goroutines in Akka. Go channels are often used as synchronization points. You cannot reproduce that directly in Akka. In Akka, post-sync processing has to be moved into a separate handler ("strewn" in jamie's words :D). I'd say the design patterns are different. You can kick off a goroutine with a chan, do some stuff, and then <- to wait for it to finish before moving on. Akka has a less-powerful form of this with ask, but ask isn't really the Akka way IMO.

Chans are also typed, while mailboxes are not. That's a big deal IMO, and it's pretty shocking for a Scala-based system. I understand that become is hard to implement with typed messages, but maybe that indicates that become isn't very Scala-like. I could say that about Akka generally. It often feels like its own thing that happens to run on Scala. Goroutines are a key reason Go exists.

Don't get me wrong; I like the actor model a lot, and I generally like Akka and find it pleasant to work in. I also generally like Go (I find Scala beautiful, while I find Go merely useful; but it is quite useful).

But fault tolerance is really the point of Akka IMO. You happen to get concurrency with that. Concurrency is the heart of goroutines. Fault-tolerance is a separate thing in Go, delegated to defer and recover, which can be used to implement quite a bit of fault tolerance. Akka's fault tolerance is more formal and feature-rich, but it can also be a bit more complicated.

All said, despite having some passing similarities, Akka is not a superset of Go, and they have significant divergence in features. Akka and Go are quite different in how they encourage you to approach problems, and things that are easy in one, are awkward, impractical, or at least non-idiomatic in the other. And that's the key differentiators in any system.

So bringing it back to your actual question: I would strongly recommend rethinking the Go interface before bringing it to Scala or Akka (which are also quite different things IMO). Make sure you're doing it the way your target environment means to do things. A straight port of a complicated Go library is likely to not fit in well with either environment.


These are all great and thorough answers. But for a simple way to look at it, here is my view. Goroutines are a simple abstraction of Actors. Actors are just a more specific use-case of Goroutines.

You could implement Actors using Goroutines by creating the Goroutine aside a Channel. By deciding that the channel is 'owned' by that Goroutine you're saying that only that Goroutine will consume from it. Your Goroutine simply runs an inbox-message-matching loop on that Channel. You can then simply pass the Channel around as the 'address' of your "Actor" (Goroutine).

But as Goroutines are an abstraction, a more general design than actors, Goroutines can be used for far more tasks and designs than Actors.

A trade-off though, is that since Actors are a more specific case, implementations of actors like Erlang can optimize them better (rail recursion on the inbox loop) and can provide other built-in features more easily (multi process and machine actors).


액터 모델에서 주소 지정이 가능한 엔티티는 메시지 수신자 인 액터라고 말할 수 있습니다. 반면 Go 채널에서 주소 지정 가능한 엔티티는 메시지가 흐르는 파이프 인 채널입니다.

Go 채널에서 채널에 메시지를 보내면 많은 수신자가 수신 할 수 있으며 그중 한 명은 메시지를받습니다.

Actor에서는 메시지를 보낸 actor-ref를 가진 한 명의 액터 만 메시지를받습니다.

참고 URL : https://stackoverflow.com/questions/22621514/is-scalas-actors-similar-to-gos-coroutines

반응형