【物語で学ぶ Swift Concurrency】actor の「再入可能性と競合状態」を楽しく理解しよう！

Swift 5.5から導入されたActorは、並行処理におけるデータ競合を防ぐための強力なツールです。しかし、Actorの再入可能性（Reentrancy） によって、意図しない競合状態（Race Condition）が発生する可能性があります。本記事では、具体例を交えながら、Actorの再入可能性と競合状態について詳しく解説します。

---

## 登場人物  
- **田中先生**: 情報科学を教える高校の先生。Swiftの並行処理について詳しい。  
- **翔太（しょうた）**: 高校2年生。プログラミング部に所属し、最近Swiftを学び始めた。  
- **彩花（あやか）**: 高校1年生。プログラミング初心者で、並行処理についてよくわかっていない。  

---

## Scene 1: プログラミングの授業開始  
（高校の情報科学の授業中。Swiftの並行処理について学んでいる。）  

**田中先生:** 「今日は、Swiftの`actor`について説明するぞ。特に、`actor`の**再入可能性（Reentrancy）** について詳しく話す。」  

**翔太:** 「先生！`actor`って確か、並行処理でデータ競合を防ぐための仕組みでしたよね？」  

**田中先生:** 「その通り！Swiftでは`actor`を使うことで、複数のスレッドが同時にデータを書き換える **データ競合（Data Race）** を防ぐことができる。」  

**彩花:** 「データ競合って、プログラムがバグる原因になるんですか？」  

**田中先生:** 「そうだ。例えば、2つの処理が同じ変数を書き換えようとすると、結果が予測できないことがある。それを防ぐのが`actor`だ。ただし……」  

**翔太:** 「ただし？」  

**田中先生:** 「`actor`には **再入可能性（Reentrancy）** があるため、場合によっては **競合状態（Race Condition）** が発生する可能性があるんだ。」  

**彩花:** 「え、どういうことですか？」  

**田中先生:** 「よし、具体的なコードを見てみよう。」  

---

## Scene 2: `actor`の再入可能性とは？  
**田中先生:** 「まず、次のコードを見てみよう。」  

```swift
actor Score {
    var localLogs: [Int] = []
    private(set) var highScore: Int = 0

    func update(with score: Int) async {
        localLogs.append(score) // ①
        highScore = await requestHighScore(with: score) // ②
    }

    func requestHighScore(with score: Int) async -> Int {
        try? await Task.sleep(nanoseconds: 2 * NSEC_PER_SEC) // 2秒待つ
        return score
    }
}
```

**彩花:** 「`update`関数は何をしてるんですか？」  

**田中先生:** 「まず、`localLogs.append(score)`でスコアを記録して、それから`await requestHighScore(with: score)`でスコアを更新している。」  

**翔太:** 「`await`を使うと、処理が止まるんですよね？」  

**田中先生:** 「そう。`await`が実行されると、処理が2秒間止まる。この間に、別の`update`が実行されたらどうなると思う？」  

**彩花:** 「え？もしかして、`localLogs.append(score)`の順番が変わる？」  

**田中先生:** 「その通り！それが **競合状態（Race Condition）** の発生原因だ。」  

---

## Scene 3: `update`を2回呼び出したら……  
**田中先生:** 「次のコードを見てみよう。`update(with:)`を2回非同期で呼び出している。」  

```swift
let score = Score()

Task.detached {
    await score.update(with: 100) // ③
    print(await score.localLogs)  // ③'
    print(await score.highScore)  // ③"
}

Task.detached {
    await score.update(with: 110) // ④
    print(await score.localLogs)  // ④'
    print(await score.highScore)  // ④"
}
```

**田中先生:** 「このコードを実行すると、結果はこうなる。」  

```text
[100, 110]
100
[100, 110]
110
```

**翔太:** 「なるほど、`localLogs`には `[100, 110]` って記録されてる……。」  

**彩花:** 「あれ？`highScore`は最初100だったのに、最後は110になってますね？」  

**田中先生:** 「そう。この`await`のせいで、実行の順番が変わることがあるんだ。」  

---

## Scene 4: `await`の前後で結果が変わる  
**田中先生:** 「今度は、`localLogs.append(score)`を`await`の後に移動してみよう。」  

```swift
func update(with score: Int) async {
    highScore = await requestHighScore(with: score) // ②
    localLogs.append(score) // ①
}
```

**田中先生:** 「実行すると、結果はこうなる。」  

```text
[100]
100
[100, 110]
110
```

**彩花:** 「あれ？さっきと違う！」  

**翔太:** 「`await`の位置を変えただけで、結果が変わってる！」  

**田中先生:** 「そう。`await`を挟むことで、処理が中断されるから、`localLogs.append(score)`が実行されるタイミングが変わるんだ。」  

---

## Scene 5: まとめ  
**田中先生:** 「じゃあ、今日のポイントをまとめよう。」  

✅ **`actor`はデータ競合を防ぐが、`await`を挟むと別の処理が割り込む可能性がある。**  
✅ **競合状態（Race Condition）は、`await`の位置によって発生し、処理結果が変わることがある。**  
✅ **並行処理を考慮して、データの更新順序に注意しないとバグの原因になる！**  

**翔太:** 「なるほど、`actor`が安全だと思っても、`await`の使い方を間違えると危険なんですね。」  

**彩花:** 「Swiftの並行処理って、思ったより奥が深いんですね……。」  

**田中先生:** 「その通り！でも、正しく使えば`actor`はとても強力なツールだ。今日学んだことを活かして、次は`Sendable`や`global actors`について学んでみよう。」  

**翔太・彩花:** 「はい、先生！」  

（授業のチャイムが鳴り、プログラミングの授業は終了した。）  

**【完】**