`
hqs7636
  • 浏览: 220928 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

On Actors and Casting 6

阅读更多
July 16, 2009


Posted by Bartosz Milewski under C++, Concurrency, D Programming Language, Erlang, Java, Multithreading, Programming, Scala, Type System
[31] Comments

Is the Actor model just another name for message passing between threads? In other words, can you consider a Java Thread object with a message queue an Actor? Or is there more to the Actor model? Bartosz investigates.

I’ll start with listing various properties that define the Actor Model. I will discuss implementation options in several languages.
Concurrency

Actors are objects that execute concurrently. Well, sort of. Erlang, for instance, is not an object-oriented language, so we can’t really talk about “objects”. An actor in Erlang is represented by a thing called a Process ID (Pid). But that’s nitpicking. The second part of the statement is more interesting. Strictly speaking, an actor may execute concurrently but at times it will not. For instance, in Scala, actor code may be executed by the calling thread.

Caveats aside, it’s convenient to think of actors as objects with a thread inside.
Message Passing

Actors communicate through message passing. Actors don’t communicate using shared memory (or at least pretend not to). The only way data may be passed between actors is through messages.

Erlang has a primitive send operation denoted by the exclamation mark. To send a message Msg to the process (actor) Pid you write:
Pid ! Msg

The message is copied to the address space of the receiver, so there is no sharing.

If you were to imitate this mechanism in Java, you would create a Thread object with a mailbox (a concurrent message queue), with no public methods other than put and get for passing messages. Enforcing copy semantics in Java is impossible so, strictly speaking, mailboxes should only store built-in types. Note that passing a Java Strings is okay, since strings are immutable.
-Typed messages

Here’s the first conundrum: in Java, as in any statically typed language, messages have to be typed. If you want to process more than one type of messages, it’s not enough to have just one mailbox per actor. In Erlang, which is dynamically typed, one canonical mailbox per actor suffices. In Java, mailboxes have to be abstracted from actors. So an actor may have one mailbox for accepting strings, another for integers, etc. You build actors from those smaller blocks.

But having multiple mailboxes creates another problem: How to block, waiting for messages from more than one mailbox at a time without breaking the encapsulation? And when one of the mailboxes fires, how to retrieve the correct type of a message from the appropriate mailbox? I’ll describe a few approaches.
-Pattern matching

Scala, which is also a statically typed language, uses the power of functional programming to to solve the typed messages problem. The receive statement uses pattern matching, which can match different types. It looks like a switch statements whose case labels are patterns. A pattern may specify the type it expects. You may send a string, or an integer, or a more complex data structure to an actor. A single receive statement inside the actor code may match any of those.
receive {
    case s: String => println("string: "+ s)
    case i: Int => println("integer: "+ i)
    case m => println("unknown: "+ m)
}

In Scala the type of a variable is specified after the colon, so s:String declares the variable s of the type String. The last case is a catch-all.

This is a very elegant solution to a difficult problem of marrying object-oriented programming to functional programming–a task at which Scala exceeds.
-Casting

Of course, we always have the option of escaping the type system. A mailbox could be just a queue of Objects. When a message is received, the actor could try casting it to each of the expected types in turn or use reflection to find out the type of the message. Here’s what Martin Odersky, the creator of Scala, has to say about it:

The most direct (some would say: crudest) form of decomposition uses the type-test and type-cast instructions available in Java and many other languages.

In the paper he co-authored with Emir and Williams (Matching Objects With Patterns) he gives the following evaluation of this method:

Evaluation: Type-tests and type-casts require zero overhead for the class hierarchy. The pattern matching itself is very verbose, for both shallow and deep patterns. In particular, every match appears as both a type-test and a subsequent type-cast. The scheme raises also the issue that type-casts are potentially unsafe because they can raise ClassCastExceptions. Type-tests and type-casts completely expose representation. They have mixed characteristics with respect to extensibility. On the one hand, one can add new variants without changing the framework (because there is nothing to be done in the framework itself). On the other hand, one cannot invent new patterns over existing variants that use the same syntax as the type-tests and type-casts.

The best one could do in C++ or D is to write generic code that hides casting from the client. Such generic code could use continuations to process messages after they’ve been cast. A continuation is a function that you pass to another function to be executed after that function completes (strictly speaking, a real continuation never returns, so I’m using this word loosely). The above example could be rewritten in C++ as:
void onString(std::string const & s) {
    cout << "string: " << s << std::endl;
}
void onInt(int i) {
    cout << "integer: " << i << std::endl;
}

receive<std::string, int> (&onString, &onInt);

where receive is a variadic template (available in C++0x). It would do the dynamic casting and call the appropriate function to process the result. The syntax is awkward and less flexible than that of Scala, but it works.

The use of lambdas might make things a bit clearer. Here’s an example in D using lambdas (function literals), courtesy Sean Kelly and Jason House:
receive(
    (string s){ writefln("string: %s", s); },
    (int i){ writefln("integer: %s", i); }
);

Interestingly enough, Scala’s receive is a library function with the pattern matching block playing the role of a continuation. Scala has syntactic sugar to make lambdas look like curly-braced blocks of code. Actually, each case statement is interpreted by Scala as a partial function–a function that is not defined for all values (or types) of arguments. The pattern matching part of case becomes the isDefinedAt method of this partial function object, and the code after that becomes its apply method. Of course, partial functions could also be implemented in C++ or D, but with a lot of superfluous awkwardness–lambda notation doesn’t help when partial functions are involved.
-Isolation

Finally, there is the problem of isolation. A message-passing system must be protected from data sharing. As long as the message is a primitive type and is passed by value (or an immutable type passed by reference), there’s no problem. But when you pass a mutable Object as a message, in reality you are passing a reference (a handle) to it. Suddenly your message is shared and may be accessed by more than one thread at a time. You either need additional synchronization outside of the Actor model or risk data races. Languages that are not strictly functional, including Scala, have to deal with this problem. They usually pass this responsibility, conveniently, to the programmer.
-Kilim

Java is not a good language to implement the Actor model. You can extend Java though, and there is one such extension worth mentioning called Kilim by Sriram Srinivasan and Alan Mycroft from Cambridge, UK. Messages in Kilim are restricted to objects with no internal aliasing, which have move semantics. The pre-processor (weaver) checks the structure of messages and generates appropriate Java code for passing them around. I tried to figure out how Kilim deals with waiting on multiple mailboxes, but there isn’t enough documentation available on the Web. The authors mention using the select statement, but never provide any details or examples.

Correction: Sriram was kind enough to provide an example of the use of select:
int n = Mailbox.select(mb0, mb1, .., timeout);

The return value is the index of the mailbox, or -1 for the timeout. Composability is an important feature of the message passing model.
Dynamic Networks

Everything I described so far is common to CSP (Communicating Sequential Processes) and the Actor model. Here’s what makes actors more general:

Connections between actors are dynamic. Unlike processes in CSP, actors may establish communication channels dynamically. They may pass messages containing references to actors (or mailboxes). They can then send messages to those actors. Here’s a Scala example:
receive {
    case (name: String, actor: Actor) =>
        actor ! lookup(name)
}

The original message is a tuple combining a string and an actor object. The receiver sends the result of lookup(name) to the actor it has just learned about. Thus a new communication channel between the receiver and the unknown actor can be established at runtime. (In Kilim the same is possible by passing mailboxes via messages.)
Actors in D

The D programming language with my proposed race-free type system could dramatically improve the safety of message passing. Race-free type system distinguishes between various types of sharing and enforces synchronization when necessary. For instance, since an Actor would be shared between threads, it would have to be declared shared. All objects inside a shared actor, including the mailbox, would automatically inherit the shared property. A shared message queue inside the mailbox could only store value types, unique types with move semantics, or reference types that are either immutable or are monitors (provide their own synchronization). These are exactly the types of messages that may be safely passed between actors. Notice that this is more than is allowed in Erlang (value types only) or Kilim (unique types only), but doesn’t include “dangerous” types that even Scala accepts (not to mention Java or C++).

I will discuss message queues in the next installment.
分享到:
评论

相关推荐

    actors in scala

    6. Scala支持的actors模型具有语言级别的内置支持,这可能包括特定的语法、库函数或构建在Scala运行时系统中的特性,例如,Scala 2.9.1版本对actors有特定的支持。 7. 书中提到的Akka版本1.2,可能是指Akka actors库...

    kafka-on-actors:用于 Scala 的基于 Akka Actors 的 Kafka 消费者生产者

    Scala 的基于 Akka Actors 的 Kafka 消费者/生产者 支持卡夫卡 0.8.2.1 概念 Kafka 的消费者本质上是阻塞的,其想法是让消费者在自己的线程池中运行,并将消息委托给在自己的线程池中运行的 worker-actor,同时实现...

    scala-actors-2.10 jar包

    scala-actors-2.10 jar包,可以解决 scala-actors-2.10以上版本带来的不兼容问题

    Concurrent Patterns and Best Practices

    To start with, you'll understand the basic concurrency concepts and explore patterns around explicit locking, lock free programming, futures & actors. Then, you'll get insights into different ...

    AkkaScala.pdf

    **Akka** is a toolkit and runtime designed for building highly concurrent, distributed, and fault-tolerant systems on the Java Virtual Machine (JVM). It leverages the actor model of computation, ...

    Wiley-Schneier on Security(2008).pdf

    Based on the provided information from "Wiley-Schneier on Security(2008).pdf," we can extract several key topics and concepts that are covered in this book. The book, authored by Bruce Schneier, is an...

    Actors in Scala

    ### Scala中的Actors模型 #### 一、Actors模型简介 在并发编程领域,Actors模型作为一种重要的数据结构模型,正逐渐成为多核时代高效并发编程的关键技术之一。该模型的核心理念是通过消息传递来进行线程间的通信,...

    Actors In Scala

    在深入讨论Scala中的Actors之前,需要先了解几个关键概念,包括Scala编程语言、面向对象与函数式编程、以及JVM平台上的并发处理。 Scala是一种高性能的编程语言,它将面向对象编程和函数式编程的优点结合在一起。它...

    Actors in Scala epub

    Actors in Scala 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 查看此书详细信息请在美国亚马逊官网搜索此书

    SocNetV-3.0.4-7751bace-windows-installer.zip

    Social Network Visualizer ...Edit actors and ties through point-and-click, analyse graph and social network properties, produce beautiful HTML reports and embed visualization layouts to the network.

    Formal Modeling: Actors, Open Systems, Biological Systems

    Formal Modeling: Actors, Open Systems, Biological Systems Essays Dedicated to Carolyn Talcott on the Occasion of Her 70th Birthday 2011

    Akka.in.Action.2016.9.pdf

    You’ll start with the big picture of how Akka works, and then quickly build and deploy a fully functional REST service out of actors. You’ll explore test-driven development and deploying and ...

    Reactive Web Applications(Manning,2016)

    The book alternates between chapters that introduce reactive ideas (asynchronous programming with futures and actors, managing distributed state with CQRS) and practical examples that show you how to...

    JActor-4.3.0.zip_JActor download_akka_akka actors

    本文将围绕JActor 4.3.0这一特定版本,探讨其作为Akka Actors在Java平台上的实现,以及如何利用这个库来提升我们的应用性能。 首先,我们需要理解Akka Actors的基本概念。Actors是Akka中的一个核心概念,它是一种...

    JavaScript中的Elixir风格actors

    6. **调度器**:在Elixir中,有一个调度器负责分配任务给各个Actor。在JavaScript中,我们可以用一个中心控制器来协调Actor之间的通信,比如处理Actor之间的依赖关系,平衡负载,或者实现负载均衡策略。 7. **Actor...

Global site tag (gtag.js) - Google Analytics