初めて
ストラクト
化された同時性は複数のTask
を同時に実行するための強いフレトプムを提供してアフリの性能と応答性を非常に上達します。
同時性の短所の一つは複数のTask
が同一なデータを同時に接近するときに問題が発生することができるし、該当接近には読むと書きTask
が混在していることでするそのような形の問題をdata raceだと言って、偶に衝突及び推測できないアフリの動作に続けられることがあります。
以前の章には複数の同時な作業の結果を順序的に処理すると関連される問題について解決策を見てみました。
残念だけどその解決策に関連されたすべての作業は同一な作業グループにに属するに限って実際に動作する。
もっと 柔軟して同時作業を始める位置に関係なく作動する解決策は Swift Actorを使うことだ。
二十五・壱 Actor
壱・一 概要
actorは一度に1つの作業だけデータに接近されるように内部変更可能状態に対する非同期接近を制御するスイフトのプリパランスタイプでで、attribute、generator他にはmethodを含まれる点でclassと非常に似ています。これはプロトコルも遵守して拡張を書できます。Actorを宣言しするときの一番大きな違い点は 'class'の代わりに'actor'だと言う単語が使われることです。
壱・ニ 宣言する
attributeとmethodを含まれる簡単なスイフトクラスは次のように宣言します。
+ actor BuildMessage3 { // class > actor
var message : String = ""
let greeting = "Hello"
func setName(name: String ){
self.message = "\(greeting) \(name)"
}
}
actorのインスタンスは
let hello = BuildMessage()
だがクラスactorの一番大きな違い点はこれは非同期関数またはTaskのクローザー内で同じ非同期コンテキスト内だけで生成とアクセスができるということだ。またactorメソッド
を呼び出しとか属性で接近するときawaitキイワード必ず使用するべきだ
func someFunction() async{
let builder = BuildMessage()
await builder.setName(name:"Jane Smith")
let message = await builder.message
print(message)
}
二十五・弍 data isolation
actorインスタンスで含まれたデータはアフリの他のコードと隔離されます。この様なisolationはインスタンスデータを変更(ここではname変数変形)するメソッド
が呼び出されるときに、他の所のコードで該当メソッド
を`呼び出されることを保証します。そうすると複数の作業が同時にデータ変更をやってみることを防止することができます。もちるんこれはメーソド呼び出すなどか属性に対する接近をできないょうに以前の作業を処理されるまで待っているべきで、await文が必要だことを意味します。
また、isloationはコードがactor可変的な内部属性を直接に変更することを防止します。例えば次の様にBuildMessageインスタンスのmessage属性に新しい値を割合うコードがあったと考えてみよう。
builder.message = "hello"
クラスインスタンスを作業するときには有効するだけど、前のコードはactorインスタンスで試行するのでえーらが出るだろう
デフォルトでactor内のすべてのメソッドと可変的な属性はisolateされたと考慮されて、awaitキーウド
を通じて呼ぶことができます。例えばgreeting文字列を返却するnonisolatedメソッドをbuildMessageactor
で追加することができます。
var builder = BuildMessage()
func asyncFunction() async {
let greeting = builder.getGreeting()
print(greeting)
}
func sysncFunction() {
let greeting = builder.getGreeting()
print(greeting)
}
この新しいメソッド
は同期、非同期コンテキスト全てでawaitキイワードなくて呼ぶことができます。
getGreeting()メソッド
は不便的なgreeting属性だけで接近するのせいでこのメーソドをnonisolatedだけで宣言することができます。
二十五・参 swift actor例題
以前の章にはデータ競争を調べてみたしコンファイラが次の様みたいなエラー目せじを発生することができるコードを作成しないすることを学びました。
Mutation of captured var 'timeStamps' in concurrently-excuting code
次の非同期コードを使用してディクショナリーオブジェクトで項目を書こうとする時次の様なエラーが発生しました。
struct ContentView: View {
func doSomething() async{
var timeStamps: [Int: Date] = [:]
await withTaskGroup(of: Void.self){group in
for i in 1...5 {
group.addTask {
timeStamps[i] = await takesTooLong()
}
}
}
}
その様の問題を避けるための一つのオプションで以前の章で実装した方法はfor-awaitルーフを使用者して非同期作業の結果を順次的に処理することだ。だがその章で見た様にactorを使用して解決することができることである。
Xcodeで ConcurrencyDemo ふろジェクトをロードしてContentView.swifファイルを編集して次のactor宣言を追加してtimeStampsデクショナりカップシュル化してデータを追加できることのメソッド
を追加してみましょう。
actor TimeStore{
var timeStamps: [Int:Date]=[:]
func addStamp(task: Int, date: Date) {
timeStamps[task]=date
}
actorを宣言したので今から doSomething()メソッド
を修正してaddStamp()メソッド
を通じて新しいタイムスタンプを追加することができます。
二十五・肆 MainActor紹介
24章でまメーインスレッド「メインキーユウ」に対して説明しまして、UIレンダリング処理と使用者いべーんとに対する応答をどう担当するかを説明しました。また、メーインスレッドでthread・blocking作業を実行するときの危険とそうすることは実行しているプログラミングのとまる原因になることについても説明しました。前で見たよりストラクト
はメーインスレッドで分離される他のスレッドで作業実行することは簡単で強いメカニズムを提供します。また説明しないものはメーインスレッドだけであるUIアプデートを実行されることです。
swiftではメインスレッドはmainActorで表現されます。これをglobalActorだと言います。なぜならメーンスレッドで実行するコードをプログラム全体でアクセスできることだからです。
アプリを開発するとき、メーンactorで実行するを欲しなコードがあるかもしれない.特に該当コードが何らかの形でUIをアプデートしなければならない場合だ。その場合@mainActor
属性を使用してコードを表現されることができる。この属性は関連作業がメーンActorで実行されるべきだとを指しるためターフ・メソッド・インスタンス・関数・クローザーで使用されることができます。例え、メーインスレッドで動作する様にクラスを構成することもできます。
+@MainActor
class TimeStore {
var timeStamps: [Int: Date] = [:]
func addStamp(task: Int, date: Date){
timeStamps[task] = date
}
}
他の方法で単一の値とか属性をメーインスレッドで従属で表示することができます。
-
class TimeStore {
+ @MainActor var timeStamps: [Int: Date] = [:]
func addStamp(task: Int, date: Date){
timeStamps[task] = date
}
}
もちろん、timeStampsデックショナリはメーンActorで割り当てられたので他のスレッドで接近できません。前のaddStamp()メソッドからデックショナリで新しい日付を追加しようすると、次の様なエラーが発生するだろう。
その問題を解決するとaddStamp()メソッドも@MainActor属性を使用者して表紙されるべきです。
class TimeStore {
@MainActor var timeStamps: [Int: Date] = [:]
+ @MainActor func addStamp(task: Int, date: Date){
timeStamps[task] = date
}
}
mainActor
のrunメソッド
はメーンスレッドで作業を実行する様に非同期コード内で呼び出すことができます。
func runExample() async {
await MainActor.run{
}
}
要約
非同期コード作成の核心部分はデータ競争を避けることです。データ競争は二つ以上の作業が同一なデータで接近して該当作業が書き作業を実行するときで発生します。此れは同時作業が同一なデータのお互い違うバージョンを照会して作業されるデータ不一致の原因になります。
データ競争を避けるための有用な道具はSwiftのActorターフです。これは構文面で動作の面でSwiftのクラスと似ているだけどカップシュル化されたデータはアフリ内の他のコードとは別居している点が違う。もしかしてインスタンスデータを変更するActorのメソッドが呼びだされると、該当メソッドがコードの上で他のところで呼び出される様にされる前にメソッドが完全に実行されることを保証する。これは複数の作業が同時に同一なテータを変更されない様に防止する。Actorメソッド呼び出すと属性に対する接近はawaitキーウドを使って呼び出レルベキです。
メーンActorは非同期コード内でメーンスレッドに対する接近を提供する特別なActorです。`@MainActor属性は関連された作業がメーンスレッドで実行されるべきだとを表すためターフ、メソッド、インスタンス、ファンション、くろーさを表示される様に使われることができます。