.NET 4.6 presenta la AsyncLocal<T> para el flujo de datos ambientales a lo largo del flujo de control asíncrono. Anteriormente usé CallContext.LogicalGet/SetData para este propósito, y me pregunto si los dos son semánticamente diferentes y de qué manera (más allá de las diferencias obvias de la API, como la tipificación fuerte y la falta de confianza en las claves de cadena).
La semántica es más o menos la misma. Ambos se almacenan en ExecutionContext y fluyen a través de llamadas asíncronas.
Las diferencias son los cambios de API (tal como los describió) junto con la capacidad de registrar una devolución de llamada para cambios de valor.
Técnicamente, hay una gran diferencia en la implementación, ya que CallContext se clona cada vez que se copia (usando CallContext.Clone ), mientras que los datos de AsyncLocal se mantienen en el diccionario ExecutionContext._localValues y solo esa referencia se copia sin ningún trabajo adicional. .
Para asegurarse de que las actualizaciones solo afecten el flujo actual cuando cambia el valor de AsyncLocal , se crea un nuevo diccionario y todos los valores existentes se copian superficialmente en el nuevo.
Esa diferencia puede ser tanto buena como mala para el rendimiento, según dónde se use AsyncLocal .
Ahora, como Hans Passant mencionó en los comentarios, CallContext se creó originalmente para la comunicación remota y no está disponible donde no se admite la comunicación remota (por ejemplo, .Net Core), por lo que probablemente se agregó AsyncLocal al marco:
#if FEATURE_REMOTING public LogicalCallContext.Reader LogicalCallContext { [SecurityCritical] get { return new LogicalCallContext.Reader(IsNull ? null : m_ec.LogicalCallContext); } } public IllogicalCallContext.Reader IllogicalCallContext { [SecurityCritical] get { return new IllogicalCallContext.Reader(IsNull ? null : m_ec.IllogicalCallContext); } } #endif Nota: también hay un AsyncLocal en el SDK de Visual Studio que es básicamente un contenedor sobre CallContext que muestra cuán similares son los conceptos: Microsoft.VisualStudio.Threading .
Me pregunto si y de qué manera los dos son semánticamente diferentes.
Por lo que se puede ver, tanto CallContext como AsyncLocal dependen internamente de ExecutionContext para almacenar sus datos internos dentro de un Dictionary . Este último parece estar agregando otro nivel de direccionamiento indirecto para las llamadas asíncronas. CallContext ha existido desde .NET Remoting y era una forma conveniente de hacer fluir datos entre llamadas asincrónicas donde no había una alternativa real, hasta ahora.
La mayor diferencia que puedo detectar es que AsyncLocal ahora le permite registrarse para recibir notificaciones a través de una devolución de llamada cuando se cambia un valor almacenado subyacente, ya sea mediante un cambio de ExecutionContext o explícitamente reemplazando un valor existente.
// AsyncLocal<T> also provides optional notifications // when the value associated with the current thread // changes, either because it was explicitly changed // by setting the Value property, or implicitly changed // when the thread encountered an "await" or other context transition. // For example, we might want our // current culture to be communicated to the OS as well: static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>( args => { NativeMethods.SetThreadCulture(args.CurrentValue.LCID); }); Aparte de eso, uno reside en System.Threading mientras que el otro vive en System.Runtime.Remoting , donde el primero será compatible con CoreCLR.
Además, no parece que AsyncLocal tenga la semántica superficial de copia en escritura que tiene SetLogicalData , por lo que los datos fluyen entre llamadas sin copiarse.
Parece haber alguna diferencia semántica en el tiempo.
Con CallContext, el cambio de contexto ocurre cuando se configura el contexto para el subproceso/tarea/método asíncrono, es decir, cuando se llama a Task.Factory.StartNew(), Task.Run() o método asíncrono.
Con AsyncLocal, el cambio de contexto (llamada de devolución de llamada de notificación de cambio) ocurre cuando el subproceso/tarea/método asincrónico realmente comienza a ejecutarse.
La diferencia de tiempo podría ser interesante, especialmente si desea que el objeto de contexto se clone cuando se cambia el contexto. El uso de diferentes mecanismos podría dar como resultado la clonación de contenido diferente: con CallContext, clona el contenido cuando se crea el subproceso/tarea secundaria o se llama al método asíncrono; pero con AsyncLocal clona el contenido cuando el subproceso/tarea/método asíncrono comienza a ejecutarse, el subproceso principal podría haber cambiado el contenido del objeto de contexto.