[Swift6] アクター分離境界 (Actor isolation boundary) を考慮したasync関数の定義パターン
アクター分離境界(以降、アクター境界とする)を意識しなければならない箇所はいくつかありますが、その際たる例はasync関数です。
この記事ではasync関数におけるアクター境界の考え方について解説します。
前回の記事でアクター境界について解説をしている為、必要に応じて参照してください。
前提
前提として、Sendable
であれば常にアクター境界を越えることができる為、アクター境界を意識する必要はありません。その為、この記事ではRequest
とResponse
というnon-Sendable
な型を使って考えます。
async関数が呼び出された時の実行スレッド
Swift6からアクターの紐付けのないasync関数の実行は、汎用Executorに割り当てられるようになりました。
その結果、実行スレッドは以下のようになります。
よって、async関数を呼び出した際の引数と返り値がアクター境界を越える必要が出てきます。
アクターのasync関数の場合
1. sendingを用いる
では、アクターのasync関数について考えます。
この場合、最もオススメの定義はsending
を用いた以下の定義です。
sending
キーワードを用いることで、引数と返り値がアクター境界を越えることができるようにすることができます。
但し、引数は呼び出し側、返り値は実装側のメンバ変数に値を保持しておくことができなくなります。
その理由については、前回の記事を参照してください。
2. GlobalActorを用いる
メンバ変数として保持したい場合は、呼び出し側と同じGlobalActorを用いる必要があります。
このようなケースであれば、そもそもアクター境界を越えることがない為、メンバ変数に保持したとしても常に同じスレッドからしかアクセスされない為、問題がありません。
アクター以外のasync関数の場合
isolationの指定をする
アクター以外の場合は呼び出し側からisolationを引き継ぐのがオススメです。
この方法をアクターに用いる場合はnonisolated
と扱われてしまい、関数内でのメンバ変数へのアクセスが困難になりますが、アクターでない場合はこの問題がありません。
但し、Sendable
のasync関数の場合は、メンバ変数に保存する場合は異なるスレッドからアクセスされる可能性がある点には注意して実装する必要があります。
@unchecked Sendable
にしている為コンパイルエラーにはなりませんが、この場合に限らず、スレッドセーフとして扱うのであれば常に意識すべき点でもあります。
一方、non-Sendable
の場合は、そもそも同じスレッドからしか呼び出されないと考えることができるので、これを考慮する必要がありません。
参考文献
記事が気に入ったらチップを送ることができます!
You can give me a cup of coffee :)
Kyash ID: soranoba
Amazon: Wish List
GitHub Sponsor: github.com/sponsors/soranoba
PayPal.Me: paypal.me/soranoba
(Updated: )