Mischen von Threads und Coroutinen in Unity3D Mobile


8

Ich hatte eine Coroutine in Unity3D, die eine Zip-Datei von einem Server heruntergeladen, in den persistenten Datenpfad extrahiert und deren Inhalt in den Speicher geladen hat. Der Fluss sah ungefähr so ​​aus:

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

Aber die ExtractZip()Methode (die die DotNetZip-Bibliothek verwendet) ist synchron, dauert zu lange und lässt mir während des Prozesses keinen Platz zum Nachgeben.

Dies führte dazu, dass die Anwendung (auf Mobilgeräten) beendet wurde, wenn ich versuchte, eine große Zip-Datei zu extrahieren. Ich gehe davon aus, dass der Haupt-Thread zu lange nicht mehr reagiert.

Ist dies etwas, von dem bekannt ist, dass es ein mobiles Betriebssystem tut (beenden Sie die App, wenn ein Frame zu lange dauert)?

Also nahm ich an, dass das Extrahieren der Zip-Datei in einem separaten Thread das Problem lösen könnte, und es scheint funktioniert zu haben. Ich habe diese Klasse erstellt:

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;
}

Und ich benutze es so:

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

Aber ich bin mir immer noch nicht sicher, ob dies der beste Weg ist, es zu implementieren, oder ob ich sperren muss _isDone(nicht zu sehr an Multithreading gewöhnt).

Kann damit etwas schief gehen / fehlt mir etwas?

Antworten:


4

Dies ist eine wirklich elegante Lösung, um Multithread-Aufgaben in einer Coroutine zusammenzufassen. Gut gemacht :)

Das Mischen von Coroutinen und Threads ist absolut sicher, vorausgesetzt, Sie sperren den Zugriff auf Ressourcen, die zwischen Ihrem Hauptthread (den die Coroutine ausführt) und den von Ihnen erstellten Arbeitsthreads gemeinsam genutzt werden, korrekt. Sie sollten _isDone in keiner Weise sperren müssen, da es immer nur vom Arbeitsthread geschrieben wird und es keinen Zwischenzustand gibt, der dazu führen könnte, dass sich der Hauptthread schlecht verhält.

Wenn Sie nach potenziellen Problemen Ausschau halten müssen, müssen Ressourcen von ExtractZip beschrieben werden

  1. gleichzeitig von einer Funktion geschrieben, die von Ihrem Hauptthread oder aufgerufen wird
  2. Wird von einer Funktion im Hauptthread gelesen und erwartet, dass sie sich in einem sicheren Zustand befindet, bevor ExtractZip abgeschlossen wird.

In diesem speziellen Fall würde ich mir Sorgen machen, dass wenn Sie nicht überprüfen, ob Sie nicht zweimal versuchen, dieselbe Datei an denselben Speicherort herunterzuladen, zwei Threads gleichzeitig ExtractZip ausführen könnten, die sich gegenseitig stören.


1

Hierfür gibt es im Asset Store eine kostenlose Lösung:

Thread Ninja

Mit ihm können Sie einfach zwischen Haupt- und Hintergrund-Thread wechseln, was gefällt:

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

Nun, ich bin auch kein großer Experte, aber ich denke, in Ihrem zweiten Beispiel blockiert die WaitForComplete () -Funktion den aufrufenden Thread weiterhin, bis der ThreadedAction-Thread abgeschlossen ist. Was Sie tun können, ist, eine Rückruffunktion an Ihren Zip-Verarbeitungsthread zu übergeben und diese Funktion aufrufen zu lassen, wenn sie fertig ist. Auf diese Weise startet Ihr Hauptthread den Zip-Thread und erledigt dann seine Arbeit (Aktualisieren der GUI, was haben Sie), und dann setzt die Rückruffunktion etwas im Hauptthread, wenn es fertig ist (wie boolean zipDone = true), und An diesem Punkt kann der Haupt-Thread darauf reagieren (Anzeige "Zip extrahiert" in der GUI usw.).


2
Es scheint so, aber WaitForComplete ist eine Unity3D-Coroutine, sodass nur eine Iteration der while-Schleife in jedem Frame ausgeführt wird und dann alles andere im Spiel aktualisiert wird. Es blockiert nicht den Thread :)
David Gouveia

-2

Sie müssen _isDone nicht sperren, aber es muss als flüchtig markiert werden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.