以下は、Swift先生と生徒の対話形式で、nonisolated(unsafe) と NSLock、DispatchQueue を使ってグローバル変数を安全に扱う方法を説明した物語です。
Swift先生:「こんにちは。今日は、グローバル変数を安全に使うためのテクニックについてお話しするよ。グローバル変数は、プログラムのどこからでもアクセスできる便利なものだけど、複数のスレッドが同時にアクセスすると『データ競合』という問題が起きるんだ。」
生徒:「データ競合ってどんな問題ですか?」
Swift先生:「たとえば、二つの処理が同じ変数に同時にアクセスして値を更新しようとすると、計算結果が変になったり、プログラムがクラッシュしたりする可能性があるんだ。通常、Swiftのコンパイラはこうした危険をチェックしてくれるけど、場合によっては自分で安全性を保証するために、コンパイラのチェックを回避したい時もあるんだ。そのときに使うのが nonisolated(unsafe)
という宣言なんだよ。」
生徒:「nonisolated(unsafe) って、どういう意味なんですか?」
Swift先生:「nonisolated(unsafe)
を使うと、コンパイラが通常行う並行処理の安全性チェックを回避できるんだ。でも、その分、プログラマ自身が外部同期機構で安全性を保証しなければならないんだよ。今日は、NSLock と DispatchQueue を使った具体的な方法を見ていこう。」
シーン1:NSLock を使った例
生徒:「NSLockって何ですか?」
Swift先生:「NSLockは、処理中に同じ変数へのアクセスを防ぐための『鍵』のようなものだよ。次の例を見てみよう。」
import Foundation
// NSLockのオブジェクトを作成
let styleLock = NSLock()
// グローバル変数。すべてのアクセスがNSLockで保護されるため、nonisolated(unsafe) を使う
nonisolated(unsafe) var supportedStyleCount = 42
// 変数の値を1増やす関数
func incrementStyleCount() {
styleLock.lock() // ロックを獲得して、他の処理が入らないようにする
supportedStyleCount += 1 // 安全に値を更新
styleLock.unlock() // ロックを解放
}
// 現在の値を取得する関数
func getSupportedStyleCount() -> Int {
styleLock.lock() // ロックを獲得してから値を読む
let count = supportedStyleCount
styleLock.unlock() // ロックを解放
return count
}
生徒:「なるほど!NSLockでロックをかけると、同時にアクセスされるのを防げるので、nonisolated(unsafe)
を使っても安全になるんですね。」
Swift先生:「その通り。NSLockを使ってアクセスが必ず排他制御されるので、コンパイラのチェックを回避しても大丈夫なんだ。」
シーン2:DispatchQueue を使った例
生徒:「NSLock以外にも、DispatchQueueという方法があると聞きました。DispatchQueueって何ですか?」
Swift先生:「DispatchQueueは、GCD(Grand Central Dispatch)の仕組みを使って、処理を順番に実行するためのキューだよ。特にシリアルキューを使えば、登録した処理が一つずつ実行されるので、同時アクセスを防げるんだ。こちらも具体的なコードで見てみよう。」
import Dispatch
// シリアルキューを作成。これにより、登録した処理は順番に実行される
let styleQueue = DispatchQueue(label: "com.example.styleQueue")
// グローバル変数にも同じく nonisolated(unsafe) を使う
nonisolated(unsafe) var supportedStyleCount = 42
// 変数の値を増やす関数
func incrementStyleCountUsingQueue() {
styleQueue.sync { // キュー上で同期的に処理を実行
supportedStyleCount += 1
}
}
// 変数の値を取得する関数
func getSupportedStyleCountUsingQueue() -> Int {
return styleQueue.sync { // 同じく、順番に処理が実行される
supportedStyleCount
}
}
生徒:「DispatchQueueを使うと、処理が必ず順番に実行されるから、複数の処理が同時に変数にアクセスしないんですね!」
Swift先生:「その通り。NSLockと同じように、DispatchQueueを利用することで、外部同期を実現し、安全にグローバル変数を操作できるんだよ。」
まとめ
Swift先生:「今日は、グローバル変数はどこからでも使える反面、複数のスレッドから同時にアクセスすると危険になるということを学んだね。そこで、nonisolated(unsafe)
を使ってコンパイラの安全性チェックを回避し、自分でNSLockやDispatchQueueを使って安全に同期処理を行う方法を見たんだ。」
生徒:「はい、先生!nonisolated(unsafe) の意味と、それを補うための外部同期機構の使い方がよく分かりました。これからは、データ競合に気をつけながらプログラムを書けそうです!」
Swift先生:「素晴らしい!この知識を活かして、安全で信頼性のあるプログラムを書いてみよう。もしまた疑問があったら、いつでも質問してね。」
以上、Swift先生と生徒の対話形式で、nonisolated(unsafe)
と外部同期機構(NSLock、DispatchQueue)を使ってグローバル変数を安全に扱う方法について説明しました。お読みいただき、ありがとうございました!