BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース データ競合のない並列言語を目指すSwift

データ競合のない並列言語を目指すSwift

原文(投稿日:2020/11/04)へのリンク

Swiftチームは、Swiftの並列性サポートの改善に向けたロードマップを公開した。最初のフェーズでasync構文とアクタを進め、第2フェーズでデータ競合(data races)とデッドロックを排除する予定である。

並列プログラミングモデルの本質は変わらず、独立したキューによるディスパッチ、コールバック経由での結果の返送によるロックフリーな操作という基本的な思想は維持される。このアプローチには、コーディング上のオーバーヘッド、効率性の欠如、エラーが発生しやすい、などの欠点もあるため、プログラマには、正確性とパフォーマンスを保証するために、適切なパターンに厳格に従うことが求められる。

そのため、ここで示されたパターンは優れていますが、これらをSwiftで表現すると、重要な構造を失い、問題を発生させることになります。解決策は、これらのパターンを言語内に取り込むことです。

コルーチンをベースとしたSwiftの基本的なasync/await構文は、次のスニペットのようなものになる。

internal func asyncMethod() async {
  result = await anotherClass.anotherAsyncMethod()
}

この構文のメリットのひとつは、クロージャ内でselfを明示的にキャプチャする必要がなくなることだ。Swiftのasync/await構文の特徴は、awaitキーワードをasync関数毎に繰り返す必要がなく、asyncコードを含む式の先頭で1回だけ指定すればよい、という点にある。

asyncawaitに加えて、actorも導入される。これは、内部状態へのすべてのアクセスをシリアライズを通じて同期するために、プライベートキューを持つクラスの定義に対して使用される、省略的表現である。

actor class AnActorClass {

  // some state

  func modifyState() async { ... }
}

上のコードを使用すると、コンパイラは、すべてのasyncメソッドをアクタのプライベートキュー上で実行されるようにすると同時に、アクタの状態に直接アクセスしようとする部分にフラグを立てる。これによって、いくつかの最適化が可能になり、パフォーマンスの向上に寄与するのだ。

クラス内のasyncメソッドを特定のグローバルアクタに結び付ける構文もサポートされる。これは、メインキューで実行する必要のあるUI操作を同期するためにUIActorを使う場合のためのもので、例えば次のように使用する。

@UIActor
class AClassDoingUIStuff {
    func doSomeUITask() async { ... }
}

第2フェーズでは、データ競合とデッドロックを回避するために、アクタの完全なアイソレーションが追加される。

アクタアイソレーションの基本的な考え方は、メモリの排他的アクセスの考え方に似ており、なおかつ、それに基づいている。Swiftの並列性のデザインは、アクタの自然なアイソレーションを起点として、オーナシップを補助的なツールとして用いるという、安全な並列性を使いやすく構成可能なものにするアプローチの提供を目標にする。

アクタはデフォルトで、基本的なアイソレーションを提供する。これはつまり、プロパティやlet定数、ローカル値など、内部状態をデータ競合から保護するということだ。

ただしUnsafeMutablePointerを使った任意のメモリ参照やグローバルメモリ、クラス参照に関するメモリ競合は、アクタ自体で防ぐことはできない。これらはアクタ間を転送することが可能であるため、データ競合を発生させる可能性があるのだ。

基本的なアクタモデルだけでも、グローバルアクタによってデータ競合の可能性を低減することは可能だ — フレームワークがすべてのオペレーションを特定のキュー上で行うように要求すれば、グローバルアクタを(UIActorと同じように)定義して、そのプロトコルで指定することが可能になる。

しかしながら、Swiftが完全なアクタアイソレーションを実現するためには、新たな機能の追加が必要になる。その中で最も重要なのは、"アクタローカル"として型を宣言する機能だろう。これがあれば、アクタ間でのアクタ型の転送が不可能になり、必要な場合にはアクタをパスする前にclone/unshareが行われることになる。

今後ある時点で、これら新機能がデフォルトで適用される予定である。すなわち、すべてのクラスが"アクタローカル"になり、グローバル変数はすべてグローバルアクタを通じて保護されるか、"actor unsafe"と宣言されることが必要になる。この変更は、言語の並列性メカニズムの中核部分に並列安全性を導入する一方で、後方互換性が失われることにもなるのだが、大きな問題になることはないとSwiftチームは考えている。

Swiftをデータ競合フリーなコンカレント言語にするロードマップには、いくつもの言語拡張が含まれている。これらは、その複雑さゆえに、いくつものリリースにまたがって実施される予定であり、完全なアクタアイソレーションの詳細はいまだ確定されていない。新たな詳細情報が公開されれば、引き続きお伝えする予定である。

この記事に星をつける

おすすめ度
スタイル

BT