As with all multithreaded applications, the biggest challenges with concurrency are deadlock, synchronisation, and shared mutable state. Different languages have gone about preventing these issues in different ways and provide tools to the developer to avoid these situations, but there seems to be a different way emerging – actors.
Actors aren’t a new idea. In fact, they were first thought of in the 1960s, and have been implemented in Akko and other languages. More recently, Kotlin introduced some support for the Actor model, and now there’s talk of Swift including Actors at some point in the future.
But what are they?
I like to learn by using metaphors, so think of them like this. Your boss has tasked you and a coworker with handwriting a wikipedia article word for word. He’s a weird guy. Anyway, he asks you and you’ve gotta do it. You can do this in two ways. Either you both share a piece of paper and pen, and you write a sentence, then your colleague writes a sentence, then you write one. This mean you’re both kinda working together, but only one of you is actually working on something at a time.
Instead, you can agree on the article, agree on who’s doing what, and then each have a separate piece of paper and a pen and start writing. Now, you’re both working concurrently on the same task. You each have a private state (what article you’re writing, how much you’re writing) which is immutable by other actors. You can also only communicate with your colleague by email, meaning that you can send messages but don’t expect them to instantly read them. Then when you’re done, you can send your paper to a third actor, and once the third actor has received both of your papers, they can put them together and give it to your boss.
What the original paper set out about messages in Actors is the following 3 things. When an actor receives a message, it can:
- Create new actors
- Message other actors that it knows about
- Designate what to do with the next message
This is essentially git, right? If two people are working on a codebase, you don’t both share a single keyboard and have goes typing on it. You pull from remote, creating a local copy, a private state. Then you each concurrently make changes to your state, read emails that contain message whenever is convenient for you, and then both push to remote where you can use a merge request to merge them together.
There’s this other concept called Futures, or whatever it’s called in your language of choice. This is just an object that returns to you immediately, that you can pass around, store, send in messages, and it’s just an object that will have the value in when the async task has finished. This is useful so that you don’t deadlock when you send a message to yourself.
That’s a high level overview of actors. To recap: Actors have a private state, mutable only by the actor by way of deciding what to do with the next message it receives. Actors can only communicate to other actors through messages, which are intrinsically asynchronous. Actors can receive new data from other actors and mutate their state based on that. Whether or not actors can ‘reply’ to messages depends on how it’s been implemented in the language. For example, Chris Latner’s high level proposal for implementing actors in Swift details how actors could respond to messages by making use of async functions. But we’ll get to that in the next article. For now I just wanted to explain a simple but powerful concept to make concurrency much less complicated.