Usar clases de tarea o tarea tiene un cuello de botella en el rendimiento que no mencionamos en artículos anteriores. En resumen, estas clases son las que lideran cuando los resultados están disponibles de inmediatoAsignación innecesaria。 Esto significa que siempre se creará una nueva Tarea u objeto de Tarea incluso si el resultado ya está disponible. Ahora, mencionamos que el concepto de async/await que usamos en artículos anteriores existe desde la versión de .NET 4.5. Esta función se ha mejorado desde C# 7 con la versión .NET 4.7 con la estructura ValueTask, que puede usarse como retorno para funciones asíncronas.
Estructura de ValueTask
La estructura ValueTask apareció por primera vez en el repositorio corefxlab en 2015. Este repositorio se utiliza para experimentar y explorar nuevas ideas que pueden o no llegar al repositorio principal de corefx. El repositorio Corefx es el repositorio donde se encuentran todas las bibliotecas base de .NET Core. Fue desarrollado y sugerido por Stephen Taub para la biblioteca System.Threading.Tasks.Channels. En ese momento, Stephen dio una breve explicación:
Dirección de la biblioteca CorefxLab:El inicio de sesión del hipervínculo es visible.
Una ValueTask es una unión distinta de una T y una Tarea, que permite a ReadAsync asignar libremente para devolver síncronamente sus valores T disponibles (a diferencia de usar Task.FromResult, que requiere la asignación de una instancia de Tarea). ValueTask es esperable, por lo que el consumo de la mayoría de las instancias es indistinguible del consumo de tareas. Mucha gente ve los beneficios de usar esta estructura, que está incluida en C# 7 como parte del paquete NuGet System.Threading.Tasks.Extensions. Así que, antes de sumergirnos en la estructura de ValueTask, examinemos el problema que utiliza para resolver. Dado que Tarea (Tarea) es un tipo de referencia, empieza porEl método asincrónico que devuelve el objeto Task significa que se asigna al heap cada vez。 Esto es necesario en muchos casos.
Sin embargo, en algunos casos, los métodos asíncronos devolven resultados inmediatamente o completamente de forma sincrónica. En estos casos, esta asignación es innecesaria y puede resultar costosa en partes críticas del código para el rendimiento. Hasta el lanzamiento de .NET 4.7, no había forma de evitarlo, ya que los métodos asíncronos tenían que devolver Task, <T>Task o void (el último solía ser indeseable). En esta versión de .NET, esto se extiende, lo que significa que un método asincrónico puede devolver cualquier tipo siempre que tenga un método GetAwaiter accesible. ValueTask es un ejemplo concreto de este tipo, y también se añadió a esta versión.
Puedes navegar por el repositorio corefx y ver la implementación completa de ValueTask, aquí tienes la sección de API que nos interesa:
Como estructura, ValueTask permite escribir métodos asíncronos que no asignan memoria durante el tiempo de ejecución síncrono. La consistencia de API del concepto async/await no se ve comprometida de esta manera. Además, esta estructura se espera por sí sola, lo que la hace fácil de usar. Por ejemplo, si ejecutamos este código simple:
En el método MultiplyAsync, estamos simulando una situación en la que queremos evitar usar Task y devolver solo un entero simple. Esto se hace en la sentencia if del método, donde básicamente comprobamos si el parámetro pasado es cero. El problema esIncluso si nuestra condición en la sentencia if es verdadera, el código anterior crea un objeto Task。 Resolvemos este problema de la siguiente manera:
ValorTarea y Tarea
Como se mencionó antes, hay dos beneficios principales al usar ValueTask:
- Mejoras en el rendimiento
- Aumentar la flexibilidad de implementación
Entonces, ¿cuáles son los números detrás de las mejoras de rendimiento? Observa este código:
Si ejecutamos este código, tarda 120ns en ejecutar el JIT. Ahora, si reemplazamos Task por ValueTask así:
Con JIT, obtendremos un tiempo de ejecución de 65 ns. Ahora bien, es cierto que debido a Task.Delay no estamos ejecutando de forma sincrónica, pero vemos una mejora en el tiempo de ejecución.
Otro beneficio que mencionamos es la mayor flexibilidad en la implementación. Ahora, ¿qué significa exactamente esto? Pues bien, las implementaciones de interfaces asíncronas que deberían sincronizarse se verán obligadas a usar Task.Run o Task.FromResult. Por supuesto, esto conduce a los problemas de rendimiento que comentamos antes. Cuando usamos ValueTask, es más probable que elijamos entre implementaciones síncronas o asíncronas. Ten en cuenta que esto podría ser una señal de que tu código no está bien diseñado si esto te ocurre a ti.
Por ejemplo, observa esta interfaz:
Supongamos que quieres llamarlo a partir de un código así:
Como usamos ValueTask en la interfaz, la implementación de la interfaz puede ser síncrona o asíncrona. Podemos obtener este beneficio básicamente saltándonos añadir algunas funciones a IT que gestionan el comportamiento de sincronización. Es mucho más fácil usar esta interfaz de esta manera. Aquí tienes una implementación síncrona de la interfaz anterior:
Aquí tienes una implementación asíncrona de la misma interfaz:
Sin embargo, debemos considerar algunos compromisos antes de usar ValueTask. Es fácil pensar que ValueTask debería usarse por defecto en lugar de Task, lo cual ciertamente no es así. Por ejemplo, aunque ValueTask nos ayuda a evitar asignaciones innecesarias cuando hay sincronización de resultados disponible, también contiene dos campos.
Es importante recordar que esta es la estructura que estamos usando aquí, lo que significa que usamos tipos de valor y todas sus cargas. Task, en cambio, es un tipo de referencia con un solo campo.Cuando usas ValueTask, tenemos más datos que procesar y procesar. Si se espera tal método en un método asincrónico, entonces el método asincrónico esLa máquina de estados también será más grande, porque almacenar toda la estructura suele requerir más espacio que almacenar una sola referencia.
Por eso la gente de Microsoft recomienda usar Task o Task como tipo de retorno por defecto para los métodos asíncronos. Solo después del análisis de rendimiento deberías considerar cambiar a ValueTask.
resumen
ValueTask es una estructura introducida en .NET 4.7 que nos ofrece muchas posibilidades para usar métodos asíncronos en .NET. Sin embargo, no es sin costes. Esto es útil para métodos críticos de rendimiento que se ejecutan de forma sincrónica. Con ellos podemos evitar asignar objetos innecesarios. Aun así, como tipo de valor, conlleva todos los problemas que suelen tener los tipos de valor. Por lo tanto, podemos beneficiarnos de esta estructura, pero debemos tener cuidado.
Dirección original:El inicio de sesión del hipervínculo es visible.
|