Skip to content

ObserveOnMainThread

1. 概要

ObserveOnMainThread は、上流からの通知(OnNext / OnErrorResume / OnCompleted)を Unity のメインスレッドに切り替えて配信する R3.Unity のオペレーターです。内部的には ObserveOn(UnityFrameProvider.Update) と同義です。

Unity の GameObjectComponent、uGUI などは基本的にメインスレッドで操作する必要があります。バックグラウンド処理や別スレッドから値が流れてくる可能性がある場合、ObserveOnMainThread を挟むことで購読側の処理を Unity の Update タイミングへ戻せます。

2. シグネチャ

csharp
public static Observable<T> ObserveOnMainThread<T>(
    this Observable<T> source)

3. マーブルダイアグラム

ObserveOnMainThread のマーブルダイアグラム

値自体は変更されません。通知の配信先だけが Unity のメインスレッド(Update)へ切り替わります。

4. サンプルコード

csharp
Observable
    .FromAsync(async ct => await LoadScoreAsync(ct))
    .ObserveOnMainThread()
    .Subscribe(score => scoreText.text = score.ToString())
    .AddTo(this);

5. 補足

SubscribeOnMainThread との違い

ObserveOnMainThread は通知の配信先を変えるだけで、ソースへの購読処理をどこで開始するかは変更しません。購読処理自体を Unity メインスレッドで実行したい場合は SubscribeOnMainThread を使用します。

観点ObserveOnMainThreadSubscribeOnMainThread
変更するものOnNext / OnErrorResume / OnCompleted の実行場所ソースへの SubscribeDispose の実行場所
主な用途GameObject / Component / UI の更新を Unity メインスレッドで行う購読開始時に Unity API を触る処理を Unity メインスレッドで開始する
Subscribe(...) 内のラムダに効くか効く直接は効かない
Unity オブジェクト更新に必要か多くの場合こちらこれだけでは不十分

特に混同しやすい点は、SubscribeOnMainThreadSubscribe(x => ...) のラムダをメインスレッドで実行するための API ではないことです。Subscribe(x => transform.position = ...)Subscribe(x => text.text = ...) を Unity メインスレッドで実行したい場合は ObserveOnMainThread を使います。

csharp
// GameObject や UI を更新したい場合
observable
    .ObserveOnMainThread()
    .Subscribe(x => transform.position = x)
    .AddTo(this);

次のように SubscribeOnMainThread だけを書いても、通知を Unity メインスレッドで受け取る意味にはなりません。

csharp
// 誤解しやすい例: Subscribe ラムダの実行場所を変える目的ではない
observable
    .SubscribeOnMainThread()
    .Subscribe(x => transform.position = x)
    .AddTo(this);

ObserveOnMainThread は、置いた位置より下流の通知処理に影響します。重い計算はメインスレッドに戻す前に済ませ、Unity オブジェクトに触る直前で ObserveOnMainThread を置くのが基本です。

csharp
observable
    .Select(x => HeavyComputation(x)) // ここはメインスレッドへ戻す前に実行
    .ObserveOnMainThread()
    .Subscribe(result => resultText.text = result.ToString())
    .AddTo(this);

反対に、ObserveOnMainThread の後に重い Select などを書くと、その処理も Unity メインスレッド上で実行され、フレーム落ちの原因になります。

csharp
observable
    .ObserveOnMainThread()
    .Select(x => HeavyComputation(x)) // Unity メインスレッドを止めやすい
    .Subscribe(result => resultText.text = result.ToString())
    .AddTo(this);

購読開始処理も Unity メインスレッドに寄せたい場合は、SubscribeOnMainThread と組み合わせます。

csharp
observable
    .SubscribeOnMainThread() // 購読開始を Unity メインスレッドへ
    .ObserveOnMainThread()   // 通知の受け取りも Unity メインスレッドへ
    .Subscribe(x => UpdateGameObject(x))
    .AddTo(this);