Mezclando hilos y corutinas en Unity3D Mobile


8

Tuve una rutina en Unity3D que descargó un zip de un servidor, lo extrajo a la ruta de datos persistente y cargó su contenido en la memoria. El flujo se parecía a esto:

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

Pero el ExtractZip()método (que usa la biblioteca DotNetZip), es sincrónico, toma demasiado tiempo y no me deja ningún lugar para ceder durante el proceso.

Esto resultó en la eliminación de la aplicación (en dispositivos móviles) cada vez que intentaba extraer un zip grande, lo que supongo se debe a que el hilo principal no responde durante demasiado tiempo.

¿Es esto algo que se sabe que hace el sistema operativo móvil (matar la aplicación si un marco tarda demasiado)?

Entonces, asumí que extraer el zip en un hilo separado podría resolver el problema, y ​​parece haber funcionado. Creé esta clase:

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

Y lo uso así:

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

Pero todavía no estoy seguro de si esta es la mejor manera de implementarlo, o si necesito bloquear _isDone(no demasiado acostumbrado a subprocesos múltiples).

¿Algo puede salir mal con esto / me estoy perdiendo algo?

Respuestas:


4

Esta es una solución realmente elegante para concluir tareas multiproceso en una rutina, bien hecho :)

Mezclar corutinas e hilos es perfectamente seguro, siempre que bloquee correctamente el acceso a los recursos compartidos entre su hilo principal (en el que se ejecuta la rutina) y los hilos de trabajo que crea. No debería necesitar bloquear _isDone de ninguna manera, ya que solo lo escribe el subproceso de trabajo y no hay un estado intermedio que pueda causar que el subproceso principal se comporte mal.

Donde debe buscar posibles problemas, es si ExtractZip escribe sobre los recursos y si

  1. simultáneamente escrito por una función que se llama desde su hilo principal o
  2. ser leída por una función en el hilo principal y se espera que esté en un estado seguro antes de que ExtractZip se complete.

En este caso particular, mi preocupación sería que si no verificas que no estás tratando de descargar el mismo archivo en la misma ubicación dos veces, podrías tener dos hilos simultáneamente ejecutando ExtractZip que interfieran entre sí.


1

Hay una solución gratuita para esto en Asset Store:

Hilo Ninja

Con él puedes cambiar fácilmente entre el hilo principal y el hilo de fondo, que le gusta:

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

Bueno, tampoco soy un experto importante, pero creo que en su segundo ejemplo, la función WaitForComplete () seguirá bloqueando el hilo de llamada hasta que se complete el hilo ThreadedAction. Lo que puede hacer es pasar una función de devolución de llamada a su subproceso de procesamiento zip y hacer que llame a esa función cuando haya terminado. De esa manera, su hilo principal inicia el hilo zip y luego continúa haciendo sus cosas (actualizando la GUI, qué es lo que tiene), y luego la función de devolución de llamada establecerá algo en el hilo principal cuando esté hecho (como boolean zipDone = true), y en este punto, el hilo principal puede reaccionar a eso (mostrar "Zip extraído" en la GUI, etc.).


2
Parece que sí, pero WaitForComplete es una rutina de Unity3D, por lo que solo ejecutará una iteración del ciclo while en cada fotograma, luego cederá para dejar que todo lo demás en el juego se actualice. No está bloqueando el hilo :)
David Gouveia

-2

No necesita bloquear _isDone, pero sí debe estar marcado como volátil.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.