スレッドの安全性の要約


この付録では、OS X および iOS の主要なフレームワークの高水準スレッドの安全性について説明します。この付録の情報は変更される可能性があります。


Cocoa


複数のスレッドから Cocoa を使用する際のガイドラインは以下のとおりです。


Foundation フレームワークのスレッドの安全性


Foundation フレームワークはスレッドセーフであり、Application Kit フレームワークはそうではないという誤解があります。残念ながら、これは全体的な一般化であり、やや誤解を招くものです。各フレームワークにはスレッドセーフな領域とスレッドセーフではない領域があります。以下のセクションでは、Foundation フレームワークの一般的なスレッドの安全性について説明します。


スレッドセーフなクラスと関数


以下のクラスおよび関数は、一般にスレッドセーフであると考えられています。最初にロックを取得することなく、複数のスレッドから同じインスタンスを使用できます。


スレッドセーフでないクラス


以下のクラスおよび関数は、一般にスレッドセーフではありません。ほとんどの場合、これらのクラスは、一度に 1 つのスレッドからのみ使用する限り、どのスレッドからでも使用できます。詳細については、クラスのドキュメントを参照してください。


NSArchiver、NSCoder、 および NSEnumerator オブジェクトはそれら自身スレッドセーフですが、使用中にそれらによって包み込まれたデータオブジェクトを変更することは安全でないため、ここにリストされています。たとえば、アーカイバ (archiver) の場合、アーカイブされるオブジェクトグラフを変更することは安全ではありません。列挙子の場合、列挙されたコレクションを変更するスレッドは安全ではありません。


メインスレッドのみのクラス


次のクラスは、アプリケーションのメインスレッドからのみ使用しなければなりません。


NSAppleScript


可変に対する不変


不変オブジェクトは一般にスレッドセーフであり、一度作成すると、これらのオブジェクトをスレッド間で安全に渡し渡されることができます。もちろん、不変オブジェクトを使用する場合は、参照カウントを正しく使用することを忘れないでください。保持していないオブジェクトを不適切に開放すると、後で例外が発生する可能性があります。


可変オブジェクトは、一般にスレッドセーフではありません。スレッド化されたアプリケーションで可変オブジェクトを使用するには、アプリケーションはロックを使用してそれらにアクセスを同期しなければなりません。(詳細については、アトミック (微少) な操作 を参照してください)。一般的に、コレクションクラス (NSMutableArrayNSMutableDictionary など) は、突然変異が関係する場合にスレッドセーフではありません。つまり、1 つ以上のスレッドが同じ配列を変更している場合、問題が発生する可能性があります。スレッドの安全性を確保するために、読み取りと書き込みが発生する場所をロックしなければなりません。


メソッドが不変オブジェクトを返すと主張したとしても、返されたオブジェクトが不変であるとは決して考えるべきではありません。メソッドの実装に応じて、返されるオブジェクトは可変または不変である可能性があります。たとえば、 NSString の戻り値の型を持つメソッドは、その実装のために NSMutableString を実際には返します。あなたが持っているオブジェクトが不変であることを保証したいならば、あなたは不変のコピーを作るべきです。


リエントラント


リエントラントは、操作が同じオブジェクト内または別のオブジェクト内の他の操作を "呼び出す" 場合にのみ可能です。オブジェクトを保持したり解放したりすることは、時に見落とされるような "呼び出し" の 1 つです。


以下の表は、明示的にリエントラントな Foundation フレームワークの部分を示しています。他のすべてのクラスは、リエントラントでないかもしれないし、将来リエントラントになるかもしれない。リエントラントに関する完全な分析は一度も行われておらず、このリストは網羅的ではありません。



クラスの初期化


Objective-C 実行環境システムは、クラスが他の何らかのメッセージを受信する前に、すべてのクラスオブジェクトに initialize メッセージを送信します。これにより、実行環境を使用する前にクラスを設定する機会が与えられます。マルチスレッドアプリケーションでは、実行環境は、最初のメッセージをクラスに送信するスレッドである唯一のスレッドのみが initialize メソッドを実行することを保証します。最初のスレッドがまだ initialize メソッドにある間に 2 番目のスレッドがクラスにメッセージを送信しようとすると、2 番目のスレッドは initialize メソッドの実行が終了するまでブロックします。その間、最初のスレッドはクラスの他のメソッドを引き続き呼び出すことができます。initialize メソッドは、クラスのメソッドを呼び出す 2 番目のスレッドに依存するべきではありません。そうであれば、2 つのスレッドはデッドロック状態になります。


OS X バージョン 10.1.x 以前のバグのために、スレッドは、別のスレッドがそのクラスの initialize メソッドの実行を終了する前に、クラスにメッセージを送信する可能性があります。スレッドは、完全に初期化されていない値にアクセスし、アプリケーションをクラッシュさせる可能性があります。この問題が発生した場合は、ロックを導入して初期化されるまで値にアクセスしないようにするか、クラスを初期化する前にマルチスレッド化する必要があります。


自動解放プール


各スレッドは、独自の NSAutoreleasePool オブジェクトのスタックを保持します。Cocoa は、現在のスレッドのスタック上で常に自動解放プールが利用できると考えています。プールが利用できない場合、オブジェクトは解放されず、メモリがリークします。NSAutoreleasePool オブジェクトは、Application Kit に基づいたアプリケーションのメインスレッドで自動的に作成および破棄されますが、二次スレッド (および Foundation のみのアプリケーション) は、Cocoa を使用する前に独自のアプリケーションを作成しなければなりません。スレッドの寿命が長く、潜在的に多くの自動開放されたオブジェクトを生成している場合は、自動解放プールを定期的に破棄して作成する必要があります (メインスレッド上で Application Kit が行なうように)。それ以外の場合は、自動開放されたオブジェクトが蓄積され、メモリ使用量が増加します。分離したスレッドが Cocoa を使用していない場合は、自動解放プールを作成する必要はありません。


実行ループ


すべてのスレッドには 1 つの実行ループしかありません。ただし、各実行ループ、したがって各スレッドには、実行ループの実行時にどの入力ソースを聴取するかを決定する独自の入力モードのセットがあります。1 つの実行ループで定義された入力モードは、同じ名前を持っていたとしても、別の実行ループで定義されている入力モードには影響しません。


メインスレッドの実行ループは、アプリケーションが Application Kit に基づいている場合は自動的に実行しますが、二次スレッド (および Foundation のみのアプリケーション) は実行ループを自分自身で実行しなければなりません。切り離されたスレッドが実行ループに入らない場合、スレッドは、切り離されたメソッドの実行が終了するとすぐに終了します。


外見にもかかわらず、NSRunLoop クラスはスレッドセーフではありません。このクラスのインスタンスメソッドは、それを所有するスレッドからのみ呼び出す必要があります。


Application Kit フレームワークのスレッドの安全性


以下のセクションでは、Application Kit フレームワークの一般的なスレッドの安全性について説明します。


スレッドセーフでないクラス


以下のクラスおよび関数は、一般にスレッドセーフではありません。ほとんどの場合、これらのクラスは、一度に 1 つのスレッドからのみ使用する限り、どのスレッドからでも使用できます。詳細については、クラスのドキュメントを参照してください。


メインスレッドだけのクラス


以下のクラスは、アプリケーションのメインスレッドからだけ使用しなければなりません。


Window の制限事項


二次スレッド上で Window を作成できます。Application Kit は、競合状態を回避するために、window に関連したデータ構造がメインスレッド上で割り当て解除されることを保証します。多くの window を同時に処理するアプリケーションで window オブジェクトがリークする可能性があります。


二次スレッドでモーダル window を作成できます。Application Kit は、メインスレッドがモーダルループを実行している間、呼び出し側の二次スレッドをブロックします。


イベント処理の制限事項


アプリケーションのメインスレッドは、イベントの処理する責任があります。メインスレッドはN SApplicationrun メソッドでブロックされたもので、通常はアプリケーションの main 関数で呼び出されます。他のスレッドがイベントパスに応答している場合、Application Kit は引き続き動作しますが、操作は順序どおりに行われません。たとえば、2 つの異なるスレッドがキーイベントに応答している場合、キーは順不同で受信できます。メインスレッドにイベントを処理させることで、より一貫したユーザーエクスペリエンスを実現できます。一度受信すると、イベントは必要に応じてさらに処理するために二次スレッドに急送できます。


二次スレッドから NSApplicationpostEvent:atStart: メソッドを呼び出して、メインスレッドのイベントキューにイベントを送信できます。ただし、ユーザーの入力イベントに関しては順序は保証されません。アプリケーションのメインスレッドは、依然としてイベントキュー内のイベントを処理する責任があります。


描画の制限事項


Application Kit は、NSBezierPath および NSString クラスを含む、グラフィックスの関数およびクラスを使用して描画する場合、一般にスレッドセーフです。特定のクラスを使用するための詳細は、以下のセクションで説明します。描画とスレッドの追加情報については、Cocoa の描画ガイド を参照してください。


NSView の制限事項


NSView クラスは一般にスレッドセーフではありません。NSView オブジェクト上での作成、破棄、サイズ変更、移動、その他の操作は、アプリケーションのメインスレッドからのみ行なう必要があります。二次スレッドからの描画は、lockFocusIfCanDrawunlockFocus への呼び出しで描画呼び出しを囲む限り、スレッドセーフです。


アプリケーションの二次スレッドがビューの一部をメインスレッド上に再描画したい場合、 displaysetNeedsDisplay:, setNeedsDisplayInRect:, あるいは setViewsNeedDisplay: のようなメソッドを使用してはいけません。代わりに、performSelectorOnMainThread:withObject:waitUntilDone: メソッドを使用してメインスレッドにメッセージを送信するか、それらのメソッドを呼び出す必要があります。


NSGraphicsContext の制限事項


NSGraphicsContext クラスは、基になるグラフィックス・システムによって提供される描画コンテキストを表します。各 NSGraphicsContext インスタンスは、独自の独立したグラフィック状態、つまり座標系、クリッピング、現在のフォントなどを保持します。クラスのインスタンスは、各 NSWindow インスタンスのメインスレッド上に自動的に作成されます。二次スレッドから何らかの描画を行なうと、NSGraphicsContext の新しいインスタンスがそのスレッド専用に作成されます。


二次スレッドから何らかの描画を行なう場合は、描画呼び出しを手動でフラッシュしなければなりません。Cocoa は二次スレッドから描画されたコンテンツではビューを自動的に更新しないため、描画を終了するときに NSGraphicsContextflushGraphics メソッドを呼び出す必要があります。アプリケーションがメインスレッドのみからコンテンツを描画する場合、描画呼び出しをフラッシュする必要はありません。


NSImage の制限事項


一つのスレッドは NSImage オブジェクトを作成でき、イメージバッファに描画し、描画のためにそれをメインスレッドに渡す事ができます。基礎となるイメージキャッシュはすべてのスレッド間で共有されます。イメージとキャッシングの仕組みの詳細については、Cocoa の描画ガイド を参照してください。


Core Data フレームワーク


Core Datra フレームワークは一般にスレッド化をサポートしていますが、適用される使用上の警告がいくつかあります。これらの警告に関する情報は、Core data プログラミングガイド のCore Data との同時処理 を参照してください。


Core Foundation


Core Foundation は十分にスレッドセーフなため、注意深くプログラムすると、競合するスレッドに関連する何らかの問題に遭遇することはないでしょう。通常の場合、不変オブジェクトの照会、保持、解放、渡す事など、スレッドセーフです。複数のスレッドから照会される可能性のある中央共有オブジェクトであっても、確実にスレッドセーフです。


Core Foundation は、Cocoa と同様に、オブジェクトやその内容に対する変異に関してはスレッドセーフではありません。たとえば、可変データまたは可変配列オブジェクトを変更することは、スレッドセーフではありませんが、予想できるように、不変配列内のオブジェクトを変更することはありません。その理由の 1 つはパフォーマンスであり、これは、これらの状況では重要です。さらに、このレベルでは絶対的なスレッドの安全性を達成することは通常不可能です。たとえば、コレクションから取得したオブジェクトを保持することによる不確定な動作などを除外することはできません。格納されたオブジェクトを保持するための呼び出しが行われる前に、コレクション自体が解放される可能性があります。


Core Foundation オブジェクトを複数のスレッドからアクセスして変異する場合は、アクセスポイントでロックを使用することにより、コードを同時アクセスから保護する必要があります。たとえば、Core Foundation 配列のオブジェクトを列挙するコードでは、列挙するブロックを囲む適切なロック呼び出しを使用して、他の誰かが配列を変異させるのを防ぐ必要があります。


前の章 次の章





目次
Xcode の新機能

  • 序言
  • この文書の構成
    以下も見よ
  • スレッドプログラミングについて
  • スレッドとは何か?
    スレッドの用語集
    スレッドの代替
  • スレッドのサポート
  • スレッドパッケージ
    実行ループ
    同期ツール
    スレッド間通信
  • デザインのヒント
  • スレッドを明示的に作成しない
    スレッドを合理的にビジー状態に保つ
    共有データ構造を避ける
    スレッドとユーザーインターフェイス
    終了時のスレッド動作に注意
    例外処理
    スレッドをきれいに終了
    ライブラリでのスレッドの安全性
  • スレッド管理
  • スレッドのコスト
  • スレッドの作成
  • NSThread の使用
    POSTIX スレッドの使用
    NSObject を使ってスレッドを生成
    POSIX スレッドを Cocoa アプリで使用
    Cocoa フレームワークの保護
    POSIX と Cocoa のロックの混在
  • スレッド属性の構成
  • スレッドのスタックサイズの構成
    スレッドローカルな格納の構成
    スレッドの切り離された状態の設定
    スレッドの優先度の設定
  • スレッドのエントリルーチンの作成
  • 自動開放プールの作成
    例外ハンドラの設定
    実行ループの設定
    スレッドの終了
  • 実行ループ
  • 実行ループの解剖学
    実行ループモード
    入力ソース
    ポートを基本としたソース
    カスタム入力ソース
    セレクターソースを Cocoa で実行
    タイマーソース
    実行ループ監視者
    イベントの実行ループシーケンス
  • 実行ループをいつ使用すべきか?
  • 実行ループオブジェクトの使用
  • 実行ループオブジェクトの取得
    実行ループの構成
    実行ループの開始
    実行ループの終了
    スレッドの安全性と実行ループオブジェクト
  • 実行ループソースの構成
  • カスタム入力ソースの定義
    入力ソースの定義
    実行ループへの入力ソースのインストール
    入力ソースのクライアントとの調整
    入力ソースへの信号
    タイマーソースの構成
    ポートを基本とした入力ソースの構成
    NSMachPort オブジェクトの構成
    NSMessagePort オブジェクトの構成
    Core Foundation でのポートを基本とした入力ソースの構成
  • 同期化
  • 同期ツール
    アトミック (微少) な操作
    メモリーバリアと揮発性変数
    ロック(Locks)
    条件
    セレクタルーチンの実行
    同期コストとパフォーマンス
    スレッドの安全性と信号
  • スレッドセーフな設計のヒント
  • 同期を一切避ける
    同期の限界を理解
    コードの正しさへの脅威に気づく
    デッドロックとライブロックを監視
    揮発性変数の正しい使用
    アトミック (微少) 操作の使用
  • ロック (Locks) の使用
  • POSIX mutex ロックの使用
    NSLock クラスの使用
    @synchronized 指令の使用
    NSRecursiveLock オブジェクトの使用
    NSConditionLock オブジェクトの使用
    NSDistributedLock オブジェクトの使用
  • 条件の使用
  • NSCondition クラスの使用
    POSIX 条件の使用
  • スレッドの安全性の要約
  • Cocoa
  • Foundation フレームワークのスレッドの安全性
    スレッドセーフなクラスと関数
    スレッドセーフでないクラス
    メインスレッドのみのクラス
    可変に対する不変
    リエントラント
    クラスの初期化
    自動解放プール
    実行ループ
    Application Kit フレームワークのスレッドの安全性
    スレッドセーフでないクラス
    メインスレッドだけのクラス
    Window の制限事項
    イベント処理の制限事項
    描画の制限事項
    NSView の制限事項
    NSGraphicsContext の制限事項
    NSImage の制限事項
    Core Data フレームワーク
  • Core Foundation
  • 用語集
  • 文書更新履歴












  • トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)












    トップへ(Thread Program Guide)