¿En qué se diferencia la semántica de AsyncLocal del contexto de la llamada lógica?


. NET 4.6 introduce la clase AsyncLocal<T> para el flujo de datos ambientales a lo largo del flujo asíncrono de control. Anteriormente he usado CallContext.LogicalGet/SetData para este propósito, y me pregunto si y de qué manera los dos son semánticamente diferentes (más allá de las diferencias obvias de API como la escritura fuerte y la falta de confianza en las claves de cadena).

Author: i3arnon, 2015-07-29

3 answers

La semántica es más o menos la misma. Ambos se almacenan en ExecutionContext y fluyen a través de llamadas asincrónicas.

Las diferencias son cambios en la API (tal como se describe) 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 extra trabajo.

Para asegurarse de que las actualizaciones solo afectan el flujo actual cuando cambia el valor de AsyncLocal, se crea un nuevo diccionario y todos los valores existentes se copian poco a poco en el nuevo.

Esa diferencia puede ser tanto buena como mala para el rendimiento, dependiendo de dónde se use el AsyncLocal.

Ahora, como Hans Passant mencionó en los comentarios CallContext se hizo originalmente para la comunicación remota, y no está disponible donde no se admite la comunicación remota (por ejemplo,. Net Core), que es probablemente la razón por la que AsyncLocal se añadió 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 wrapper sobre CallContext que muestra lo similares que son los conceptos: Microsoft.VisualStudio.Threading .

 33
Author: i3arnon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-03 14:00:12

Me pregunto si y de qué manera los dos son semánticamente diferentes{[14]]}

Por lo que se puede ver, tanto CallContext como AsyncLocal internamente dependen de ExecutionContext para almacenar sus datos internos dentro de un Dictionary. Este último parece estar agregando otro nivel de indirección para llamadas asincrónicas. CallContext ha existido desde.NET Remoting y era una forma conveniente de fluir datos entre llamadas asincrónicas donde no había una alternativa real, hasta ahora.

La mayor diferencia que puedo detectar es eso AsyncLocal ahora le permite registrarse en las notificaciones a través de una devolución de llamada cuando se cambia un valor almacenado subyacente, ya sea por un interruptor 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á soportado en CoreCLR.

Además, no parece que AsyncLocal tenga la semántica superficial de copy-on-write que tiene SetLogicalData, por lo que los datos fluyen entre llamadas sin ser copiados.

 16
Author: Yuval Itzchakov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-20 09:19:35

Parece haber alguna diferencia semántica en el tiempo.

Con CallContext el cambio de contexto ocurre cuando se configura el contexto para el método thread/task/async hijo, es decir, cuando Task.Fábrica.StartNew (), Task.Se llama a Run() o al método async.

Con AsyncLocal el cambio de contexto (se llama a la devolución de llamada de notificación de cambio) ocurre cuando el hilo hijo/tarea/método async realmente comienza a ejecutarse.

La diferencia de tiempo podría ser interesante, especialmente si desea que el objeto context se clone cuando se cambia el contexto. El uso de diferentes mecanismos podría dar lugar a que se clone contenido diferente: con CallContext clona el contenido cuando se crea el hilo hijo/tarea o se llama al método async; pero con AsyncLocal clona el contenido cuando el hilo hijo/tarea/método async comienza a ejecutarse, el contenido del objeto de contexto podría haber sido cambiado por el hilo padre.

 2
Author: WenningQiu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-14 20:33:00