4種類目の型、Actorによる並行処理で競合状態を解決
SwiftにActorが実装されました。Actorを使うと後述する並行処理の問題をより安全に解決することができます。
Actorはclass、struct、およびenumに続く4種類目のSwiftの型です。Actorはclassと同じく参照型であり、またprotocolの適合やextensionによる拡張という他の型と同じ操作が可能です。Actorを定義する際には下記のように定義します。
actor Counter { ... }
Actorの利点
まずは並行処理をプログラムする際の競合状態の問題と、Actorがどのようにその問題を解決するかを説明します。
競合状態の問題
並行処理の歴史的な問題として競合状態があります。これは、異なる2つ以上のスレッドが同時に同じデータに読み書きをしようとするデータ競合と呼ばれる現象によって起こります。下記の例では、Counterインスタンスの2回目のincrement()メソッドの呼び出しによって、2が出力されることを期待していますが、2つのincrement()メソッドが同時に実行されたため1が出力されてしまったことを示しています。

このような問題があるため複数のスレッドから同じ可変変数にアクセスする際にはAtomics、Locks、およびSerial dispatch queueという技術が使われていました。しかし、これらの解決方法にも安全に可変変数を読み書きできるかはプログラマー次第であり、慎重にコーディングする必要があるという課題がありました。
Actorによる解決
Actorを使って先述したコードを書き直したのが下記のコードになります。このようにActorを使うと、Actorが持つプロパティやメソッドへのアクセスにはawaitキーワードが必要となり、非同期なアクセスとなります。これは、Actorへのアクセスは2つ以上のスレッドから同時に行えないというActorの特徴からくるものであり、現在アクセス中のスレッドがある場合に別のスレッドからアクセスするとそのアクセスを待機することを示しています。つまり、Actorを使うと同時に1つのスレッドからしかActor内の可変変数にアクセスできなくなるため、データ競合が発生しなくなります。

さらに、Actorを使うとデータ競合が起こりそうなコードがコンパイルエラーとなるため、ランタイム時に意図しない挙動になるのを予防できます。例えば、Actor変数の更新を別スレッドから行おうとするとコンパイルエラーとなります。
MainActor
MainActorと呼ばれる特別なActorが存在します。MainActorでは定義されたメソッドの実行やプロパティの読み書きが必ずメインスレッド上にて行われることを保証します。MainActorを使用するには、下記のように@MainActorを使って型をやメソッドを定義します。ある型に対して@MainActorを使った場合はその型のプロパティやメソッド全てに適応され、プロパティやメソッドに対して@MainActorを使った場合はその箇所だけに適応されます。
@MainActor class AnyUI { .... }
@MainActor func updateUI() { ... }
@MainActor var count = 0
下記の2つの理由から標準APIにあるUIViewやUIViewControllerなどのクラスとMainActorは相性が良いです。実際に、今回のアップデートから標準でUIViewやUIViewControllerはMainActorでマークされています。
- UIViewやUIViewControllerはUIの描画を行う
- Viewの更新は必ずメインスレッドから行う必要がある
なお、MainActorはメインスレッドが既に実行中の処理がある場合に、メインスレッド以外から受け取ったメッセージは直ちに実行せずに一旦待機させます。そのためMainActorの呼び出しは非同期処理となるため、await anyUI.updateUI()というようにawaitキーワードが必要です。
おわりに
Swift Concurrencyと一口に言ってもasync/await構文やActorなど、内包する概念はいくつもあります。本記事はWWDC21にて発表されていた内容を基に、それぞれに対して背景や提供される機能についてまとめました。Swift Concurrencyによって今後の開発体験が向上する期待感が高まっただけではなく、今まで気づかなかった並行処理に関する問題について考える機会にもなったのではないでしょうか。
次回の記事では、Xcode Cloud、およびSwiftUIについてWWDC21にて発表されている内容をまとめる予定です。お楽しみに。
