Introducción a AsyncLocal en .NET

Conceptos fundamentales de AsyncLocal

AsyncLocal es un tipo diseñado para almacenar y transmitir estado en entornos asíncronos. Permite compartir datos entre hilos o tareas sin verse afectado por los cambios de contexto asíncrono.

Cada flujo asíncrono posee sus propios datos independientes

  • AsyncLocal se utiliza principalmente para compartir objetos dentro del mismo flujo de control asíncrono, por ejemplo: una solicitud web que atraviesa múltiples llamadas a métodos async/await (posiblemente cambiando de hilo varias veces) puede seguir compartiendo el mismo objeto.
  • AsyncLocal presenta una característica de anidamiento jerárquico, a diferencia de ThreadLocal que permanece fijo en un solo hilo. AsyncLocal opera sobre flujos de control asíncronos de tipo arbóreo.
    class Aplicacion
    {
        private static AsyncLocal<contextosolicitud> contextoAsincrono = new AsyncLocal<contextosolicitud>();

        static void Main(string[] args)
        {
            //Simular 5 solicitudes HTTP concurrentes
            for (var i = 0; i < 5; i++)
            {
                var indice = i;
                Task.Factory.StartNew(async () =>
                {
                    var ctx = contextoAsincrono.Value = new ContextoSolicitud();
                    ctx.NombreSolicitud = "Solicitud" + indice;
                    ctx.Identificador = indice;
                    Console.WriteLine($"Antes del retardo  IdHilo:{Thread.CurrentThread.ManagedThreadId} ctx.Nombre={ctx.NombreSolicitud} ctx.Id={ctx.Identificador}");
                    await Task.Delay(new Random().Next(1000, 2000));
                    Console.WriteLine($"Después del retardo IdHilo:{Thread.CurrentThread.ManagedThreadId} ctx.Nombre={ctx.NombreSolicitud} ctx.Id={ctx.Identificador}");
                });
            }
            Console.Read();
        }
    }

    class ContextoSolicitud
    {
        public string NombreSolicitud { get; set; }
        public int Identificador { get; set; }
    }
</contextosolicitud></contextosolicitud>

Comportamiento de AsyncLocal en flujos de control asíncronos arbóreos

  • Cada nodo puede tener su propio objeto.
  • Cuendo un nodo hijo no establece un objeto, accede al objeto del nodo padre.
  • Cuando un nodo hijo establece un objeto, accede al objeto que él mismo configuró.
  • El nodo padre no puede acceder a los objetos establecidos por los nodos hijos.
    class Aplicacion
    {
        private static AsyncLocal<contextosolicitud> contextoFlujo = new AsyncLocal<contextosolicitud>();

        static async Task Main(string[] args)
        {
            await MetodoPadre();
            Console.Read();
        }

        //Contexto del nivel superior
        public static async Task MetodoPadre()
        {
            contextoFlujo.Value = new ContextoSolicitud
            {
                Identificador = 0,
                NombreSolicitud = "Padre"
            };
            Console.WriteLine("Padre:" + contextoFlujo.Value);
            await MetodoHijo();
            Console.WriteLine("Padre:" + contextoFlujo.Value);
        }

        //Contexto secundario
        public static async Task MetodoHijo()
        {
            Console.WriteLine("Hijo - Antes:" + contextoFlujo.Value);
            contextoFlujo.Value = new ContextoSolicitud
            {
                NombreSolicitud = "Hijo",
                Identificador = 1,
            };
            Console.WriteLine("Hijo - Después de modificar");
            Console.WriteLine("Hijo - Actual:" + contextoFlujo.Value);
        }
    }

    class ContextoSolicitud
    {
        public string NombreSolicitud { get; set; }
        public int Identificador { get; set; }

        public override string ToString()
        {
            return $"Nombre={NombreSolicitud},Id={Identificador}";
        }
    }
</contextosolicitud></contextosolicitud>

Escenarios de uso de AsyncLocal

Transmisión de datos de estado: En operaciones asíncronas, como métodos asíncronos o cadenas de tareas, necesitamos compartir ciertos datos de estado. Utilizando AsyncLocal, podemos transmitir estos datos entre operaciones asíncronas sin necesidad de pasarlos explícitamente como parámetros.

Información contextual relcaionada: En ocasiones, necesitamos acceder a información contextual a través de métodos o tareas asíncronas, como información de autenticación de usuarios o configuración de idioma. Con AsyncLocal, podemos acceder a esta información en toda la pila de llamadas asíncronas sin necesidad de pasarla como parámetro en cada método.

    //En la misma solicitud web, obtener datos del contexto del comerciante
    //serán idénticos y no afectarán a otras solicitudes web
    public class ContextoActual
    {
        private static readonly AsyncLocal<usuarioactual> UsuarioActual = new AsyncLocal<usuarioactual>();

        public static void EstablecerDatosActuales(UsuarioActual usuario)
        {
            UsuarioActual.Value = usuario;
        }

        public static UsuarioActual ObtenerDatosActuales()
        {
            return UsuarioActual.Value ?? new UsuarioActual();
        }
    }
</usuarioactual></usuarioactual>

Etiquetas: .NET C# async AsyncLocal programación-asíncrona

Publicado el 5-30 18:09