NOTE 過去に同時並行コードを書いたことがある場合は、スレッドの操作に慣れているかもしれません。Swiftの 同時並行モデルはスレッドの上に構築されていますが、直接スレッドとやり取りすることはありません。Swift の非同期関数は、実行中のスレッドを放棄することができ、最初の関数がブロックされている間、そのスレッド上で別の非同期関数を実行できます。
throws
を使用するのと同様に、パラメータの後に async
キーワードをその宣言に書きます。関数またはメソッドが値を返す場合は、戻り値の型の矢印(->
)の前に async
を書いてください。例えば、ギャラリ内の写真の名前を取得する方法が次のとおりです:throws
の前に async
を書きます。await
を書きます。これは、スロー関数を呼び出すときにエラーが発生した場合にプログラムの流れを変更する可能性を示すために try
を書くのと同様です。非同期メソッド内では、別の非同期メソッドを呼び出す場合にのみ、実行フローが中断されます。中断は決して暗黙的に処理を割り込むものではありません。つまり、全ての中断する可能性があるポイントには await
をマークします。listPhotos(inGallery:)
と downloadPhoto(named:)
関数は両方ともネットワークリクエストをする必要があり、完了するまでかなり時間がかかる可能性があります。戻り値の型の矢印の前に async
を書くことで両方とも非同期にすると、このコードが写真を取得するのを待っている間も、他のアプリのコードは実行し続けることができます。await
まで実行されます。listPhotos(inGallery:)
関数を呼び出し、その関数が返されるのを待つ間、実行を中断しますawait
が書かれた次の中断ポイント、または処理が完了するまで実行されますlistphotos(inGallery:)
から戻り値が返された後、その地点から処理は再開され photoNames
に戻り値を割り当てますsortedNames
と name
を定義する行は、通常の同期処理です。これらの行では何もマークされていないため、中断される可能性はありませんawait
は、downloadPhoto(named:)
関数の呼び出し時に示されています。このコードは、その関数が戻り値を返すまで再び実行を一時中断し、他の同時並行処理を実行する機会を与えますdownloadPhoto(named:)
が戻り値を返し、その戻り値が photo
に割り当てられ、show(_:)
を呼び出すときに引数として渡されていますawait
でマークされた、コード内で処理が中断する可能性のあるポイントは、非同期関数またはメソッドが戻るのを待っている間に、現在のコードが処理を一時中断することを示しています。内部では、Swift は、現在のスレッド上のコードの実行を中断し、代わりにそのスレッド上の他のコードを実行するため、これはスレッドを譲る(yielding the thread)と呼ばれます。await
時にコードを中断できるようにする必要があるため、プログラム内の特定の場所だけが非同期関数またはメソッドを呼び出すことができます:@main
でマークされている構造体、クラス、または列挙型の static main()
メソッド内NOTETask.sleep(nanoseconds:)
メソッドは、同時並行処理が機能する方法を学ぶために簡単なコードを書くときに役立ちます。このメソッドは何もしませんが、それがリターンする前に少なくとも指定されたナノ秒数処理を待ちます。下記は、ネットワーク操作の待機をシミュレートするためにsleep(nanoseconds:)
を使用するlistPhotos(inGallery:)
関数のバージョンです。1func listPhotos(inGallery name: String) async throws -> [String] {2try await Task.sleep(nanoseconds: 2 * 1_000_000_000) // 2 秒3return ["IMG001", "IMG99", "IMG0404"]4}Copied!
listPhotos(inGallery:)
は、非同期に配列全体を一度にまとめて返し、全ての配列の要素は既に取得できています。もう 1 つの方法として、非同期シーケンス(asynchronous sequence)を使用して、コレクションの要素を 1 つずつ待機することができます。下記では非同期シーケンスを使って配列の要素を 1 つ 1 つ取得しています:for-in
ループを使用する代わりに、上記の例は for
の後に await
を書きます。非同期関数またはメソッドを呼び出すときのように、await
は中断する可能性を示します。for-await-in
ループは、次の要素が利用可能になるのを待っている間、各繰り返しの開始時に処理の実行を中断する可能性があります。for-in
ループで独自の型を使用できるのと同じように、AsyncSequenceプロトコルに準拠することで、for-await-in
ループで独自の型を使用できます。await
を使用して非同期関数を呼び出すと、一度に 1 つのコードしか実行されません。非同期コードが実行されている間、呼び出し側は、次のコード行を実行する前にそのコードが終了するのを待ちます。例えば、ギャラリから最初の 3 つの写真を取得するには、次のように downloadPhoto(named:)
関数を 3 回呼び出して、結果を待つことができます:downloadPhoto(named:)
の呼び出しは一度に 1 つの呼び出ししか実行されません。つまり、次の写真のダウンロードを開始する前に、各写真のダウンロードの完了を待ちます。しかし、これらの操作を待機する必要はありません。各写真の処理は独立して、同時にダウンロードできます。let
の前に async
を書き、この定数を使用する度に await
を書きます。downloadPhoto(named:)
の 3 つの呼び出しは、前のものが完了するのを待たずに開始します。利用可能な十分なシステムリソースがある場合は、これらは同時並行に実行できます。これらの関数呼び出しには await
がマークされていないため、関数の結果を待つためにコードが中断されません。代わりに、photos
が定義されている行まで処理は継続し、その時点で、プログラムはこれらの非同期呼び出しの結果を必要とするため、3 つの写真のダウンロードが完了するまで実行を一時中断するため、 await
を書きます。await
を使用して非同期関数を呼び出します。これにより、順番に実行されるタスクが作成されますasync-let
を使用して非同期関数を呼び出すと、同時並行に実行できるタスクが作成されますawait
と async-let
のどちらも、中断されている間に他のコードを実行できるようにしますawait
を使って中断ポイントを示しますasync-let
構文は子タスク(child task)を作成します。タスクグループ(task group)を作成し、そのグループに子タスクを追加することもできます。こうすることで、グループでの優先順位とキャンセルをより制御しやすくし、動的な数のタスクを作成することもできます。CancellationError
のようなエラーをスローするnil
または空のコレクションを返すCancellationError
をスローするTask.checkCancellation()を呼び出すか、Task.isCancelledの値を確認し、自分でコードのキャンセルを処理します。例えば、ギャラリから写真をダウンロードしているタスクは、一部だけダウンロードされたデータを削除してネットワークを切断する必要があるかもしれません。{}
)の定義の前に actor
キーワードを付けてアクターを導入します TemperatureLogger
アクターには、外部の他のコードがアクセスできるプロパティがあり、max
プロパティはアクター内のコードのみが最大値を更新できるように制限されています。await
を使用します。例えば:logger.max
へのアクセスは中断する可能性があります。アクターはその可変状態にアクセスすることを一度に 1 つのタスクのみに制限しているため、別のタスクからのコードが既にロガーとやり取りしている場合、このコードはプロパティへのアクセスを待機します。await
を記載しません。例えば、下記は TemperatureLogsger
を新しい温度で更新するメソッドです:update(with:)
メソッドは既にアクター上で実行されているので、max
のようなプロパティへのアクセスに await
を書きません。また、このメソッドは、なぜアクターが一度に 1 つのタスクしかそれらの可変状態とやり取りすることを許さないのかの理由の 1 つを示しています: アクターの状態の更新は一時的に不変性を壊します。TemperatureLogger
アクターは温度のリストと最大温度を追跡し、新しい測定を記録するときに最高温度を更新します。アップデートの途中で、新しい測定値が追加されても、max
が更新される前の状態だと、温度ロガーは一時的に矛盾した状態になります。複数のタスクが同じインスタンスと同時にやり取りする問題を防ぐことで、次のような問題を防ぎます:update(with:)
を呼び出して、まず measurements
配列を更新しますmax
を更新する前に、他の場所で最大値と気温の配列を読み取りますmax
を変更することによって更新を終了しますupdate(with:)
の途中で割り込んでアクターへアクセスされた場合、他の場所で実行されているコードが不正な情報を読み取るかもしれません。Swift のアクターを使用すると、それらの状態に対して一度に 1 つの操作のみアクセス可能なことと、await
をマークした場所でのみ中断される可能性があるため、この問題を防ぐことができます。update(with:)
では中断ポイントが含まれていないため、他のコードは update
の途中で他のコードからデータにアクセスできません。await
を書かないと logger.max
へのアクセスは失敗します。Swift は、アクター内のコードのみがアクターの隔離されたローカルの状態にアクセスできることを保証します。この保証はアクターの隔離(actor isolation)と呼ばれています。