En el motor de juego Unity3D, una secuencia de código común para obtener datos remotos es la siguiente:
WWW www = new WWW("http://remote.com/data/location/with/texture.png");
yield return www;
¿Cuál es el mecanismo subyacente aquí?
Sé que utilizamos el mecanismo de rendimiento para permitir que se procese el siguiente fotograma, mientras se completa la descarga. ¿Pero qué sucede debajo del capó cuando hacemos el yield return www
?
¿Qué método se llama (si corresponde, en la clase WWW)? ¿Está Unity usando hilos? ¿La capa de Unidad "superior" se está apoderando de la instancia de www y está haciendo algo?
EDITAR:
- Esta pregunta es específicamente sobre los componentes internos de Unity3D. No me interesan las explicaciones de cómo
yield
funciona la declaración en C #. En cambio, estoy buscando una visión interna de cómo Unity trata con estas construcciones, para permitir, por ejemplo, a WWW descargar una pieza de datos de manera distribuida en varios marcos.
yield return
para operaciones asincrónicas es un hack. En un programa "real" de C #, usaría unTask
para esto. Es probable que Unity no los use porque se creó antes de .Net 4.0, cuandoTask
se introdujo.Respuestas:
Esta es la palabra clave de rendimiento de C # en acción: no está haciendo nada especial con el
www
objeto, sino que significa algo especial para el método que contiene. Específicamente, esta palabra clave solo se puede usar en un método que devuelve unIEnumerable
(oIEnumerator
), y se usa para indicar qué objeto será "devuelto" por el enumerador cuando se invoca MoveNext .Funciona porque el compilador convierte todo el método en una clase separada que implementa
IEnumerable
(oIEnumerator
) usando una máquina de estado; el resultado neto es que el cuerpo del método en sí no se ejecuta hasta que alguien enumera a través del valor de retorno. Esto funcionará con cualquier tipo, no tiene absolutamente nada de especialWWW
, sino que es el método que lo contiene lo que es especial.Eche un vistazo a Detrás de escena de la palabra clave de rendimiento de C # para obtener más información sobre qué tipo de código genera el compilador de C #, o simplemente experimente e inspeccione el código usted mismo usando algo como IL Spy
Actualización: para aclarar
yield return
declaración, todo lo que sucede es que se devuelve un enumerador; en este punto, no se ejecuta ninguno de los cuerpos del métodoMoveNext
al iterador para obtener el primer valor en la secuencia. Esto hace que el método se ejecute hasta la primerayeild return
instrucción, momento en el que se reanuda la llamada (y presumiblemente Unity continúa para representar el resto del marco)MoveNext
método en el iterador una vez por cada cuadro posterior, lo que hace que el método se ejecute nuevamente hasta la siguienteyield return
declaración una vez por cada cuadro, hastayield break
que se alcanza el final del método o una declaración (lo que indica el final de la secuencia)El único bit especial aquí (y en un par de otros casos ) es que Unity no avanza este iterador en particular el siguiente fotograma, sino que solo avanza el iterador (haciendo que el método continúe ejecutándose) cuando se completa la descarga. Aunque parece haber una clase YieldInstruction base que presumiblemente contiene un mecanismo genérico para indicar a Unity cuándo se debe avanzar un iterador, la
WWW
clase no parece heredar de esta clase, por lo que solo puedo suponer que hay un caso especial para Esta clase en el motor de Unity.Para ser claros, la
yield
palabra clave no hace nada especial para laWWW
clase, sino que es el manejo especial que Unity le da a los miembros de la enumeración devuelta lo que causa este comportamiento.Actualiza el segundo: cuanto al mecanismo que
WWW
usa para descargar páginas web de forma asincrónica, probablemente use el método HttpWebRequest.BeginGetResponse que usará internamente IO asincrónico o, alternativamente, podría usar hilos (ya sea creando un hilo dedicado o usando un grupo de hilos).fuente
WWW
objeto cuando se entrega, veaWWW
la referencia .yield
parece ser utilizado principalmente en Unity en un contexto de rutina. Para leer más acerca de las corutinas y por qué usan C #,yield
recomiendo este artículo de blog: las corutinas Unity3D en detalle . La mayor parte de la investigación en esta respuesta proviene de ese artículo.Las rutinas en Unity se utilizan para encapsular tareas que:
Ejemplos de este tipo de tareas son los (re) cálculos de búsqueda de ruta o, como es el caso en su pregunta, obtener datos de un sitio web.
Para responder a sus preguntas (en un orden ligeramente modificado):
La
WWW
clase de Unity está diseñada para ser producida a partir de una rutina. De acuerdo con los comentarios en el artículo del blog vinculado anteriormente, el bloque de código especulativo (la capa "superior") sobreYieldInstruction
s en realidad contiene un interruptor que también verifica si hayWWW
s producidos . Este código luego se asegura de que la rutina termine automáticamente cuando se realiza la descarga, como se describe enWWW
la referencia .En este caso, descargar los datos "sin bloquear el resto del juego": sí, lo más probable. (Y el enhebrado definitivamente se usa para descomprimir los datos descargados, como lo demuestra
WWW.threadPriority
).fuente
Desafortunadamente, WWW se implementa internamente como código nativo, lo que significa que no podemos ver el código. Por experimentación puedo decir que
WWW
se no se deriva deYieldInstruction
, así que lo que sucede cuando ustedyield
debe ser manejada por el código-caso especial.Nunca he observado ninguna diferencia entre
y
Creo que esa es la forma más lógica de implementarlo, y muy probablemente eso es lo que está sucediendo debajo del capó. Pero no estoy seguro.
Unity no inicia un nuevo hilo para descargar, al menos en algunas plataformas (iOS, jugador web). O si lo hace, se establece
WWW.isDone
en el hilo principal. Lo sé porque este código:no funciona
No creo que pueda tener respuestas más específicas a menos que alguien con acceso al código fuente de Unity3d venga aquí.
fuente
Dado que Unity3D usa C # como su motor de secuencias de comandos, supongo que es la palabra clave de rendimiento estándar que está integrada en C #. Básicamente, lo que significa es que ya devuelve el valor de www para que pueda continuar mientras que en la próxima iteración devolverá el siguiente valor, etc. El rendimiento básicamente crea una máquina de estado y un iterador en segundo plano.
fuente
WWW
la referencia . Además, no estoy seguroyield
crea nada. El contexto del iterador se crea al implementarloIEnumerable
o usarlo como un tipo de retorno. La "máquina de estado" también parece estar apagada. Claro, hay estado, pero eso por sí solo no es una propiedad suficiente, ¿verdad? Tal vez podrías dar más detalles sobre eso.