Unity3D Mobileでのスレッドとコルーチンの混合


8

Unity3Dにコルーチンがあり、サーバーからzipをダウンロードして永続データパスに抽出し、その内容をメモリにロードしました。フローは次のようになります。

IEnumerator LongCoroutine()
{
    yield return StartCoroutine(DownloadZip());
    ExtractZip();
    yield return StartCoroutine(LoadZipContent());
}

しかし、このExtractZip()方法(DotNetZipライブラリを使用)は同期的であり、時間がかかりすぎて、プロセス中に処理する場所がありません。

これにより、大きなzipを抽出しようとすると(モバイルデバイスで)アプリケーションが強制終了されました。これは、メインスレッドが長時間応答しなくなったことが原因と考えられます。

これはモバイルOSが行うことが知られていることですか(フレームに時間がかかりすぎる場合はアプリを終了します)?

したがって、別のスレッドでzipを抽出すると問題が解決する可能性があると想定しましたが、うまくいったようです。私はこのクラスを作成しました:

public class ThreadedAction
{
    public ThreadedAction(Action action)
    {
        var thread = new Thread(() => {
            if(action != null)
                action();
            _isDone = true;
        });
        thread.Start();
    }

    public IEnumerator WaitForComplete()
    {
        while (!_isDone)
            yield return null;
    }

    private bool _isDone = false;
}

そして私はそれを次のように使用します:

IEnumerator LongCoroutine()
{
    yield return StartCoroutine(DownloadZip());
    var extractAction = new ThreadedAction(ExtractZip);
    yield return StartCoroutine(extractAction.WaitForComplete());
    yield return StartCoroutine(LoadZipContent());
}

しかし、これがそれを実装するための最良の方法であるかどうか、またはロックする必要があるかどうかはまだ_isDoneわかりません(マルチスレッドにあまり慣れていない)。

これで何か問題が発生する可能性がありますか/何か不足していますか?

回答:


4

これは、コルーチンでマルチスレッドタスクをラップするための非常に洗練されたソリューションです。

メインスレッド(コルーチンが実行されるスレッド)と作成したワーカースレッドの間で共有されるリソースへのアクセスを正しくロックすれば、コルーチンとスレッドの混在は完全に安全です。_isDoneは、ワーカースレッドによってのみ書き込まれ、メインスレッドの誤動作を引き起こす可能性のある中間状態がないため、いかなる方法でもロックする必要はありません。

潜在的な問題を探す必要があるのは、ExtractZipによってリソースが書き込まれ、

  1. メインスレッドから呼び出されている関数によって同時に書き込まれた、または
  2. メインスレッドの関数によって読み取られ、ExtractZipが完了する前に安全な状態であることが期待されます。

この特定の場合、私の心配は、同じファイルを同じ場所に2度ダウンロードしないことを確認しないと、ExtractZipを同時に実行している2つのスレッドが相互に干渉する可能性があることです。


1

Asset Storeには、このための無料のソリューションがあります。

スレッド忍者

これを使用すると、メインスレッドとバックグラウンドスレッドを簡単に切り替えることができます。

void Awake() {
    this.StartCoroutineAsync(AsyncCouroutine());
}

IEnumerator AsyncCoroutine() {
    // won't block
    Thread.Sleep(10000);

    yield return Ninja.JumpToUnity;

    // we're now on Unity's main thread
    var time = Time.time;

    yield return Ninja.JumpBack;

    // now on background thread again
    // ...

-1

まあ、私も主要な専門家ではありませんが、2番目の例では、WaitForComplete()関数は、ThreadedActionスレッドが完了するまで呼び出しスレッドをブロックします。できることは、コールバック関数をzip処理スレッドに渡し、完了時にその関数を呼び出させることです。そうすることで、メインスレッドがzipスレッドを開始し、その後、処理(GUIの更新など)を続行し、コールバック関数が完了すると、メインスレッドに何かを設定します(ブールzipDone = trueなど)。この時点で、メインスレッドはそれに反応できます(GUIに「Zip抽出」と表示されます)。


2
それはそのように見えますが、WaitForCompleteはUnity3Dコルーチンであるため、フレームごとにwhileループの1回の反復のみを実行し、ゲーム内の他のすべてを更新できるようにします。それはスレッドをブロックしていません:)
David Gouveia

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.