Docs Menu
Docs Home
/ /
Atlas Device SDK
/ /

スレッド化 - .NET SDK

項目一覧

  • 従う 3 つのルール
  • スレッド間の通信
  • Realm の更新
  • 非同期書込み
  • 凍結されたオブジェクト
  • Realm のスレッドモデルの詳細
  • Gitとの比較と対比
  • 内部構造
  • 概要

C#/.NET アプリを高速で応答性を高くするには、ビジュアルを配置し、ユーザー インタラクションを処理するために必要なコンピューティング時間と、データを処理し、ビジネス ロジックの実行に必要な時間のバランスを取る必要があります。 通常、アプリ開発者はこの作業を複数のスレッドに分散します。そのため、ユーザー インターフェイスに関連するすべての作業のメイン スレッドまたは UI スレッドと、提示のために UI スレッドに送信する前に、より重いワークロードを計算するための 1 つ以上のバックグラウンド スレッドがあります。 重い作業をバックグラウンド スレッドにオフロードすることで、ワークロードのサイズに関係なく、UI スレッドの応答性が非常に高くなります。 しかし、デッドロックや競合状態などの問題を回避する、スレッドセーフでパフォーマンスが高く、維持可能なマルチスレッド コードを記述するのは非常に困難です。 Realm はこれを簡素化することを目的としています。

重要

SyncronizationContext スレッド

このページでは全体を通して、「メインスレッド」(または「UI スレッド」)と「バックグラウンドスレッド」を指します。 より正確に言うと、メイン スレッドまたは UI スレッドを指す場合は、 SynchronizationContext を持つ任意のスレッドを指します また、 がないスレッドはバックグラウンドSynchronizationContext スレッドと見なされます。

Realm のマルチスレッド アプリ用ツールを調べる前に、次の 3 つのルールを理解し、従う必要があります。

読み取りをロックしないでください。
Realm のMultiversion Concurrency Control (MVCC)アーキテクチャにより、読み取り操作のためにロックする必要がなくなります。 読み込んだ値が破損したり、部分的に変更された状態になったりすることはありません。 ロックやミューテックスを必要とせずに、任意のスレッドで同じ Realm ファイルから自由に読み取りができます。 各スレッドは読み取りを行う前にすべてのスレッドを待機する必要があるため、不必要にロックするとパフォーマンスのボトルネックが発生します。
UI スレッドでの同期書込みを避けます。
Realm ファイルにはどのスレッドからでも書き込みができますが、一度に書き込みできるのは 1 つだけです。 同期書込みトランザクションは相互にブロックされます。 そのため、メイン スレッドで同期書込み (write) を行うと、バックグラウンド スレッドでの書込みが完了するのを待機している間にアプリが応答しなくなる可能性があります。 これを防ぐために、SDK はWriteAsync()メソッドを提供します。 詳細については、「非同期書込み 」を参照してください。
ライブ オブジェクト、コレクション、または Realm を他のスレッドに渡さないでください。
ライブ オブジェクト、コレクション、Realm インスタンスはスレッド定義です。つまり、これらは作成されたスレッドでのみ有効です。 具体的には、ライブ インスタンスを他のスレッドに渡すことはできません。 ただし、Realm にはスレッド間でオブジェクトを共有するためのいくつかのメカニズムが用意されています。

異なるスレッドから同じ Realm ファイルにアクセスするには、アクセスが必要なすべてのスレッドで Realm インスタンスをインスタンス化する必要があります。 As long as you specify the same configuration, all realm instances will map to the same file on disk.

マルチスレッド環境で Realm を使用する際の重要なルールの 1 つは、 オブジェクトはスレッド定義であることです。他のスレッドで発生した Realm、コレクション、またはオブジェクトのインスタンスにはアクセスできません。 Realm のMultiversion Concurrency Control (MVCC)アーキテクチャでは、オブジェクトのアクティブなバージョンが常に多数存在する可能性があります。 スレッド制限により、そのスレッド内のすべての インスタンスが同じ内部バージョンになります。

スレッド間での通信が必要な場合は、ユースケースに応じていくつかのオプションがあります。

  • 2 つのスレッド上のオブジェクトを変更するには、両方のスレッドでオブジェクトをクエリします。

  • React任意のスレッドで行われた変更に対応するには、Realm の 通知 を使用し 。

  • 現在のスレッドの Realm インスタンス内の別のスレッドに発生した変更を確認するには、Realm インスタンスを更新します。

  • オブジェクトの高速で読み取り専用のビューを他のスレッドに送信するには、オブジェクトを「フリーズ」します。

  • アプリ内でオブジェクトの多くの読み取り専用ビューを保持して共有するには、Realm からオブジェクトをコピーします。

メインの UI スレッド(または実行ループを持つ任意のスレッド)では、Realm はすべての実行ループ反復の開始時にオブジェクトを自動的に更新します。 ループの反復処理の合間に、スナップショットを操作するため、個々のメソッドは常に一貫したビューを参照し、他のスレッドで何が起こるかを心配する必要はありません。

スレッドでRealmを初めて開くと、その状態は最新の成功した書込みコミットの状態になり、更新されるまでそのバージョンは維持されます。 スレッドに実行ループがない場合(バックグラウンド スレッドでは通常、)、トランザクションを最新の状態に進めるためにRealm.Refresh()メソッドを手動で呼び出す必要があります。

書込みトランザクションがTransaction.Commit() でコミットされると、Realm も更新されます。

注意

Realm を定期的に更新できないと、一部のトランザクション バージョンが「固定」され、そのバージョンで使用されるディスク領域が再利用されなくなり、ファイル サイズが大きくなる可能性があります。

WriteAsync()メソッドは、UI スレッドをオフロードする簡単な方法を提供します。 Realm はトランザクションを非同期に開始してコミットしますが、実際の書込みブロックは元のスレッドで実行されます。 したがって、変更を待機することは非同期ですが、コールバックはメイン スレッドで実行されます。 つまり、書込みブロックが より前に作成したオブジェクトとクエリは、スレッドセーフな参照に依存することなく ブロック内で使用できます。

次のコードは、 AsyncWrite()を使用してオブジェクトを作成する 2 つの例を示しています。

var testItem = new Item
{
Name = "Do this thing",
Status = ItemStatus.Open.ToString(),
Assignee = "Aimee"
};
await realm.WriteAsync(() =>
{
realm.Add(testItem);
});
// Or
var testItem2 =
await realm.WriteAsync(() =>
{
return realm.Add<Item>(new Item
{
Name = "Do this thing, too",
Status = ItemStatus.InProgress.ToString(),
Assignee = "Satya"
});
}
);

注意

バックグラウンド スレッドでWriteAsync()を呼び出すと、Realm はスレッド上で同期的に実行されるため、 Write() を呼び出すのと同じになります。

スレッドで区切られたライブ オブジェクトはほとんどの場合正常に動作します。 ただし、一部のアプリ(リアクティブなイベントストリームベースのアーキテクチャに基づくものなど)では、UI スレッドで最終的に終了する前に、処理のために不変のコピーを多数のスレッドに送信する必要があります。 毎回深いコピーを作成するとコストが高くなるため、Realm ではライブ インスタンスをスレッド間で共有できません。 この場合、オブジェクト、コレクション、Realm をフリーズできます。

凍結により、ディスク上にまだ存在する特定のオブジェクト、コレクション、または Realm の不変のビューが作成され、他のスレッドに渡されるときに深くコピーする必要はありません。 スレッドの問題に関係なく、固定されたオブジェクトをスレッド間で自由に共有できます。

固定されたオブジェクトを操作している場合、次のいずれかを実行しようとすると例外がスローされます。

  • 固定された Realm で書込みトランザクションを開始する。

  • 固定されたオブジェクトを変更します。

  • 固定された Realm、コレクション、またはオブジェクトへの変更リスナーの追加。

一度固定すると、オブジェクトを解凍することはできません。 オブジェクトが固定されているかどうかは、 IsFrozenメソッドを使用して確認できます。 このメソッドは常にスレッドセーフです。

固定されたオブジェクトを変更するには、停止していない Realm でそのオブジェクトをクエリしてから変更します。

凍結されたオブジェクトはライブではなく、自動的に更新されません。 実質的に、停止時にオブジェクトの状態のスナップショットです。

Realm を固定すると、その子オブジェクトも固定されます。

凍結されたオブジェクトは、それを生成したライブ環境が開いている限り有効のままです。 したがって、すべてのスレッドが固定されたオブジェクトの処理を完了するまで、ライブ邦土を閉じないようにします。 ライブ Realm が閉じられる前に、固定された Realm を閉じることができます。

重要

固定オブジェクトのキャッシュについて

固定オブジェクトがキャッシュされすぎると、Realm ファイルのサイズに悪影響が及ぶ可能性があります。 「多すぎる」は、特定のターゲット デバイスと Realm オブジェクトのサイズによって異なります。 多数のバージョンをキャッシュする必要がある場合は、Realm から必要なものをコピーすることを検討してください。

Realm は マルチバージョン同時実行制御 (MVCC) により、安全で高速、ロックフリーの同時アクセスを提供します。 アーキテクチャ。

Git のような分散バージョン管理システムに理解がある場合 、MVCC について直感的に理解しているかもしれません。Git の 2 つの基本要素は次のとおりです。

  • アトミックな書込みである コミット 。

  • ブランチはコミット履歴の異なるバージョンです。

同様に、Realm にはトランザクションの形式でアトミックにコミットされた書込み (write) があります。 Realm には、ブランチと同様に、常に多くの異なるバージョンの履歴があります。

フォークを通じて分散と相違をアクティブにサポートする Git とは異なり、Realm には常に最新バージョンが 1 つだけあり、常にその最新バージョンのヘッドに書込まれます。 Realm は以前のバージョンに書込むことはできません。 これは理にかなっています。データは1つの最新バージョンの真実に集約される必要があります。

Realm は B+ ツリー を使用して実装されています データ構造。最上位の ノードは Realm のバージョンを表します。子ノードは、そのバージョンの Realm 内のオブジェクトです。 Realm には最新バージョンへのポインターがあります。これは、Git が ヘッドコミット へのポインターを持っているのと同様です。

Realm はコピーオンライト手法を使用して 分離 を確保します と の 耐久性 。変更を加えると、Realm は書き込みのためにツリーの関連部分をコピーします。 その後、Realm は 2 つのフェーズで変更をコミットします。

  • Realm は変更をディスクに書き込み、成功を確認します。

  • 次に、Realm は最新バージョンのポインターを新しく書込まれたバージョンを指すように設定します。

この 2 段階のコミット プロセスにより、書込み (write) が部分的に失敗した場合でも、ツリーの関連する部分のコピーに対して変更が行われていたため、元のバージョンは一切破損しないことが保証されます。 同様に、Realm のルートポインターは、新しいバージョンの有効性が保証されるまで、元のバージョンを指します。

次の図は、コミット プロセスを示しています。

Realm は書き込み用にツリーの関連部分をコピーし、ポインターを更新して最新バージョンに置き換えます。
クリックして拡大します
  1. Realm はツリーとして構成されています。 Realm には最新バージョン V1 へのポインターがあります。

  2. 書き込み時に、Realm は V1 に基づいて新しいバージョン V2 を作成します。 Realm は変更対象のオブジェクトのコピー(A 1 、C 1 )を作成しますが、変更されていないオブジェクトへのリンクは引き続き元のバージョン(B、D)を指します。

  3. コミットを検証した後、Realm はポインターを新しい最新バージョン(V2)に更新します。 その後、Realm はツリーに接続されなくなった古いノードを破棄します。

Realm はメモリ マッピングなどのゼロコピー メソッドを使用してデータを処理します。 Realm から値を読み取る場合、ほぼそのコピーではなく、実際のディスク上の値が表示されます。 これがライブ オブジェクトの基盤です。 これは、ディスクへの書き込みが検証された後に、Realm ヘッダー ポインターを新しいバージョンを指すように設定できる理由でもあります。

  • Realm では、次の 3 つのルールに従う場合、簡単かつ安全なマルチスレッド コードが有効になります。

    • 読み取りをロックしないでください

    • バックグラウンド スレッドで書き込みを行う場合やDevice Syncを使用する場合に、UI スレッドでの同期書き込みを回避する

    • ライブ オブジェクトを他のスレッドに渡さないでください。

  • ユースケースごとにスレッド間でオブジェクトを共有する適切な方法があります。

  • Realm インスタンス内の他のスレッドに加えられた変更を確認するには、「ループ」スレッドに存在しない、または自動更新が無効になっている Realm インスタンスを手動で更新する必要があります。

  • リアクティブなイベントストリームベースのアーキテクチャに基づくアプリでは、オブジェクト、コレクション、Realm をフリーズして、シャットダウンしたコピーを処理用の別のスレッドに効率的に渡すことができます。

  • Realm のマルチバージョン同時実行制御(MVCC)アーキテクチャは、Git の と同様です。 Git と違い、Realm では各 Realm に対して正確な最新バージョンが 1 つだけです。

  • Realm は 2 つのステージでコミットされ、分離と耐久性を保証します。

戻る

削除