Actor in Swift 5.5

Tram Ho

SE-0306 introduces Actors, which are conceptually similar to safe classes for use in Concurrent tasks. This is possible because Swift ensures that the mutable state inside your Actor is only accessed by a single thread at any given time, which eliminates many fatal errors at compile time. Today I will show you how to use this tool

To explain the problem, let’s refer to the code that creates the class RiskyCollector help trade cards between collectors:

In a single-threaded environment the above code should be fine. we check if our deck contains the card in question, remove it, then add it to another collector’s deck. However, in a multithreaded environment, our code implicitly has a race condition, which is a problem whereby the results of our code will be different when two separate parts of our code run side by side.

If we call function send(card:to:) more than once at the same time, the following scenarios are possible: 1. The first thread checks if the card is in the deck and it continues. 2. The second thread also checks if the card is in the deck, and it continues. 3. The first thread removes the card from the deck and passes it on to someone else. 4. The second thread tries to remove the card from the deck, but it’s actually gone so nothing will happen. However, it still transfers the card to the other player.

In that situation, one player loses one card while the other wins two, and if that card is Black Lotus from Magic the Gathering you’ve got a big problem!

Actors solve this problem by introducing Actor isolation: stored properties and methods cannot be read from outside the Actor object unless they are implemented asynchronously and the properties are storage cannot be writable from outside the Actor object at all. Asynchronous behavior has no implementation; instead it’s because Swift automatically places these requests in a queue to be processed sequentially to avoid race conditions.

So we can rewrite RiskyCollector for SafeCollector as follows:

There are a few things to note in that example: 1. The actor is created with the new Actor keyword. This is a new concrete nominal type in Swift that combines structs, classes, and enums. 2. The send() method is marked as asynchronous, as it will need to pause its work while waiting for the transfer to complete. 3. Although the method tranfer(card : ) is not marked as asynchronous, we still need to call it with await because it will wait until another SafeCollector agent can process the request.

Just to be clear, an Actor can use its own properties and methods freely, asynchronously or otherwise, but when interacting with another Actor, it must always be done non-destructively. synchronized. With these changes Swift can ensure that all state separate from the Actor is never accessed concurrently and more importantly this is done at compile time for safety.

Actors and Classes have some similarities: – Both are reference types, so they can be used for shared state. They can have methods, properties, initializers, and subscripts. – They can follow protocols and generics. – All static properties and methods behave the same in both types, because they have no concept of self and are therefore not isolated.

Apart from Actor isolation, there are two other important differences between Actors and Classes: – Actors currently do not support inheritance, which makes their initializers much simpler – no initializers are needed create convenience, override, final keyword and more. This may change in the future. – All Acotrs fully follow a new Actor protocol; no other concrete type can use this. This allows you to restrict other parts of your code so that it can only work with Actors.

Wish you successful application of Actor in your code!

Share the news now