2015-07-29 10 views
31

.NET 4.6 introduce la classe AsyncLocal<T> per il flusso dei dati ambientali lungo il flusso asincrono del controllo. In precedenza ho usato CallContext.LogicalGet/SetData per questo scopo, e mi chiedo se e in che modo i due sono semanticamente diversi (al di là delle ovvie differenze API come la tipizzazione forte e la mancanza di affidamento sulle chiavi stringa).In che modo la semantica di AsyncLocal differisce dal contesto della chiamata logica?

+4

Non credo ci sia. È un'alternativa per progetti che non possono dipendere da CallContext perché hanno come target CoreCLR. CallContext richiede il supporto remoto, non disponibile nella versione CLR piccola. –

+2

Dov'è @ stephen-cleary quando hai bisogno di lui? – batwad

risposta

26

La semantica è praticamente la stessa. Entrambi sono memorizzati nel ExecutionContext e passano attraverso chiamate asincrone.

Le differenze sono le modifiche API (proprio come hai descritto) insieme alla possibilità di registrare un callback per le modifiche di valore.

Tecnicamente, c'è una grande differenza nella realizzazione come il CallContext viene clonato ogni volta che viene copiato (utilizzando CallContext.Clone) mentre i dati s' il AsyncLocal è conservato nel dizionario ExecutionContext._localValues e proprio questo riferimento viene copiato senza alcun lavoro supplementare .

Per assicurarsi che gli aggiornamenti influiscano solo sul flusso corrente quando si modifica il valore di AsyncLocal, viene creato un nuovo dizionario e tutti i valori esistenti vengono copiati in superficie in quello nuovo.

Questa differenza può essere positiva o negativa per le prestazioni, a seconda di dove viene utilizzato lo AsyncLocal.

Ora, come Hans Passant accennato nei commenti CallContext era originariamente per la comunicazione remota, e non è disponibile dove la comunicazione remota non è supportato (ad esempio Net Core), che è probabilmente il motivo per AsyncLocal è stato aggiunto al quadro:

#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: c'è anche un AsyncLocal in Visual Studio SDK che è fondamentalmente un wrapper sopra CallContext che mostra come simili i concetti sono: System.Threading.AsyncLocal.

15

sto chiedendo se e in che modo i due sono semanticamente differenti

Da quanto si vede, sia CallContext e AsyncLocal internamente relè ExecutionContext per memorizzare i dati interni in un Dictionary. Quest'ultimo sembra aggiungere un altro livello di riferimento per le chiamate asincrone. CallContext è in giro da .NET Remoting ed era un modo conveniente per far scorrere i dati tra le chiamate asincrone dove non esisteva una vera alternativa, fino ad ora.

La differenza più grande che posso riconoscere è che AsyncLocal ora consente di registrare per le notifiche tramite un callback quando un valore memorizzato sottostante viene modificato, sia da un interruttore ExecutionContext o esplicitamente sostituendo un valore esistente.

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

Oltre a questo, uno risiede in System.Threading mentre le altre vite in System.Runtime.Remoting, dove il primo sarà supportato in CoreCLR.

Inoltre, non sembra che lo AsyncLocal abbia la semantica copy-on-write poco profonda SetLogicalData, quindi i dati fluiscono tra le chiamate senza essere copiati.

0

Sembra esserci una differenza semantica nel tempo.

Con CallContext la modifica del contesto avviene quando viene impostato il contesto per il metodo figlio/task/asincrono, ad esempio quando vengono chiamati il ​​metodo Task.Factory.StartNew(), Task.Run() o async.

Con AsyncLocal, la modifica del contesto (modifica chiamata callback di notifica in corso) si verifica quando il metodo figlio/argomento/asincrono inizia effettivamente l'esecuzione.

La differenza di temporizzazione potrebbe essere interessante, soprattutto se si desidera che l'oggetto di contesto venga clonato quando si cambia il contesto. L'utilizzo di diversi meccanismi potrebbe causare la clonazione di diversi contenuti: con CallContext si clona il contenuto quando viene creato il thread/attività secondario o viene chiamato il metodo async; ma con AsyncLocal cloni il contenuto quando il metodo figlio/argomento/asincrono inizia l'esecuzione, il contenuto dell'oggetto contesto potrebbe essere stato modificato dal thread padre.

+0

Interessante. C'è uno snippet di codice veloce che potresti pubblicare per dimostrare questa differenza? – ChaseMedallion

+0

Storicamente (prima di .Net 4.6) abbiamo dovuto hackerare uno slot speciale su CallContext in modo che la struttura dei dati contestuali sia clonata quando viene cambiato il contesto della chiamata. Il programma dimostrativo mostra che con CallContext la clonazione avviene sul thread chiamante, rispetto a AsyncLocal il gestore di notifica delle modifiche viene richiamato sul thread chiamato. https://1drv.ms/u/s!AuD-2O_ZRWVijzXBVJTKbQeWCTzc – WenningQiu

Problemi correlati