Let us now look at another example of combining distribution and multi-threading. And this is distributed actors. So the notion of an actor, which is covered in the concurrency class, is that it is an object that has a mailbox, it has some local state, and it has some methods, and it has an active thread of control that can logically receive a message at a time from the mailbox, process a method to perform the work desired by that message, and update and read the local state as needed. So this is an actor. And we can design many concurrent algorithms to run on a multi core computer with large numbers of these actors. One such well know algorithm is called the Sieve of Eratosthenes, and this goes back to a centuries old algorithm designed by a Greek philosopher named Eratosthenes, who said that you can identify prime numbers by first filtering out the non-multiples of 2, then the non-multiples of 3, of 5, and so on. So the way the algorithm would work is that you start by giving it all the numbers, let's say starting with two, that are candidates for primes. And the first box. You start with one box which is actually an actor, to filter out the non-multiples of two. That means, you pass through the odd numbers and hold back the even numbers. So after this box, the numbers that will flow through are three, five, seven, and all the odd numbers. You use the 3 to create a new actor to filter out the non-multiples of 3. And basically, pass through 5, 7, not 9, but 11, and so on. The 5 leads to the creation of a new actor, which filters the non-multiples of 5. And will pass through 7, 11, 13, and so on. So, you sort of get the idea that this is a pipeline of actors. Each of these boxes can be implemented using the actor paradigm. And you have inherent concurrency because as you are streaming more numbers through, each of these actors can be doing some work to check for the divisibility, to see if the input number is a multiple or not. Now, the interesting question is how can we leverage distribution over here. So if we have large numbers of actors, maybe thousands, it will be too much to run them all on a single node. So what we would like to do essentially, is start putting future actors, like the ones that filter the non-multiples of 7 on a different node. So the interesting thing about the actor model, is since it was inherently based on a mailbox, it has a notion of messages built in but there's some conveniences that occur when you try to use actors within a single node. And as a result, there's some extra work you have to do when deploying actors across multiple nodes. In particular, here are the steps that you'll have to do in a typical distributed actors system. You'd have to first create a configuration file. That tells you for each actor, what the host and port is for that actor to receive messages remotely. You have to have the ability to create remote actors. So for example this actor that's filtering non multiples of five, when seven is the first actor it will have to create a remote actor, an actor on node one to start processing non-multiples of seven. So you need that capability. You need to be able to look up remote actors by some kind of logical name. So when you have multiple actors on a single node you can be exchanging object references and you don't have to worry about names of the actors. When it's distributed, well, the Internet doesn't directly help with understanding object references across different nodes so you have to use some kind of name and then be able to look up, for example, if an actor on node zero wanted to look up the actor for non multiples of seven, it would need to use some kind of string or some kind of name to be able to refer to it and send remote messages to it. And finally, messages need to be serialized. So when you have multiple actors within a single node, even though there's a logical message, you could just be passing a reference to the message object, whereas if it has to cross a node boundary, just as we learned in client-server programming, you have to serialize the data structures in the objects and send it across the node boundary. And it will, of course, have to be deserialized when it's read at the other end. But beyond these steps, the essential pattern of actors can remain unchanged. And there's a nice separation of concerns over here. You can create an actor-based algorithm, debug it on one node and when you're ready to scale it up across multiple nodes, you can work on figuring out which actors you want to assign to different nodes. For example, if we assigned actors 2, 5, 11 to node 0 and 3, 7, and 13 to node 1, we'll have much more internode communication than the version that we have right now where we only have one edge crossing the node boundaries. So, these are important considerations but they can easily be changed by changing the configuration file since you have the general ability to create and look up remote actors. So, there you have it. Actors are a very elegant way of combining multi-threading and distribution. And I hope you'll have the opportunity to try out a distributed actor model using one of the systems that is widely available.