2015-07-29 1 views
31

.NET 4.6 introduit la classe AsyncLocal<T> pour l'acheminement des données ambiantes le long du flux de contrôle asynchrone. J'ai précédemment utilisé CallContext.LogicalGet/SetData à cet effet, et je me demande si et de quelle manière les deux sont sémantiquement différents (au-delà des différences évidentes d'API comme le typage fort et le manque de confiance sur les clés de chaîne).En quoi la sémantique d'AsyncLocal diffère-t-elle du contexte d'appel logique?

+4

Je ne pense pas qu'il y en ait. C'est une alternative pour les projets qui ne peuvent pas dépendre de CallContext car ils ciblent le CoreCLR. CallContext nécessite un support à distance, non disponible dans la petite version CLR. –

+2

Où est @ stephen-cleary quand vous avez besoin de lui? – batwad

Répondre

26

La sémantique est à peu près la même. Les deux sont stockés dans le ExecutionContext et passent par des appels asynchrones. Les différences sont des changements d'API (comme vous l'avez décrit) ainsi que la possibilité d'enregistrer un rappel pour les changements de valeur.

Techniquement, il y a une grande différence dans la mise en œuvre en tant que CallContext est cloné chaque fois qu'il est copié (en utilisant CallContext.Clone) alors que les données de AsyncLocal est conservé dans le dictionnaire ExecutionContext._localValues et juste cette référence est copié sans travail supplémentaire . Pour vous assurer que les mises à jour n'affectent que le flux actuel lorsque vous modifiez la valeur de AsyncLocal, un nouveau dictionnaire est créé et toutes les valeurs existantes sont copiées superficiellement dans le nouveau.

Cette différence peut être à la fois bonne et mauvaise pour la performance, selon l'endroit où le AsyncLocal est utilisé.

Maintenant, comme Hans mentionné dans les Passant commentaires CallContext a été initialement faite pour Remoting, et n'est pas disponible où Remoting est pas pris en charge (par exemple .Net de base) qui est probablement la raison pour laquelle AsyncLocal a été ajouté au cadre:

#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 

note: il y a aussi un AsyncLocal dans le SDK Visual studio qui est essentiellement une enveloppe sur CallContext qui montre la similitude des concepts sont: System.Threading.AsyncLocal.

15

Je me demande si et de quelle manière les deux sont sémantiquement différentes

D'après ce que l'on peut voir, à la fois CallContext et AsyncLocal relais interne sur ExecutionContext pour stocker leurs données internes à l'intérieur d'un Dictionary. Ce dernier semble ajouter un autre niveau d'indirection pour les appels asynchrones. CallContext a été autour depuis. NET Remoting et était un moyen pratique de flux de données entre les appels asynchrones où il n'y avait pas une véritable alternative, jusqu'à présent. La plus grande différence que je peux constater est que AsyncLocal vous permet maintenant d'enregistrer des notifications via un rappel lorsqu'une valeur stockée sous-jacente est modifiée, soit par un commutateur ExecutionContext, soit explicitement en remplaçant une valeur existante.

// 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); 
}); 

Autre que cela, l'un réside dans System.Threading tandis que les autres vies à System.Runtime.Remoting, où l'ancien sera pris en charge dans CoreCLR.

De plus, il ne semble pas que AsyncLocal ait une sémantique de copie à l'écriture peu profonde SetLogicalData, donc les données circulent entre les appels sans être copiées.

0

Il semble y avoir une certaine différence sémantique dans le temps. Avec CallContext, le changement de contexte se produit lorsque le contexte de la méthode enfant/tâche/asynchrone enfant est configuré, c'est-à-dire lorsque la méthode Task.Factory.StartNew(), Task.Run() ou async est appelée. Avec AsyncLocal, le changement de contexte (appelé callback de notification de changement) se produit lorsque la méthode fils/tâche/asynchrone enfant commence réellement à s'exécuter. La différence de temps peut être intéressante, surtout si vous voulez que l'objet de contexte soit cloné lorsque le contexte est changé. L'utilisation de mécanismes différents peut entraîner le clonage de contenu différent: avec CallContext, vous clonez le contenu lorsque le thread/tâche enfant est créé ou que la méthode async est appelée; mais avec AsyncLocal vous clonez le contenu lorsque la méthode enfant thread/task/async commence à s'exécuter, le contenu de l'objet de contexte pourrait avoir été modifié par le thread parent.

+0

Intéressant. Y a-t-il un extrait de code rapide que vous pourriez poster qui démontre cette différence? – ChaseMedallion

+0

Historiquement (avant .Net 4.6), nous devions pirater un emplacement spécial sur CallContext afin que la structure de données contextuelle soit clonée lorsque le contexte d'appel est commuté. Le programme de démonstration montre qu'avec CallContext le clonage se produit sur le thread appelant, comparé à AsyncLocal le gestionnaire de notification de changement est appelé sur le thread appelé. https://1drv.ms/u/s!AuD-2O_ZRWVijzXBVJTKbQeWCTzc – WenningQiu