Подтвердить что ты не робот

Как семантика AsyncLocal отличается от контекста логического вызова?

.NET 4.6 вводит класс AsyncLocal<T> для текущих окружающих данных по асинхронному потоку управления. Я ранее использовал CallContext.LogicalGet/SetData для этой цели, и мне интересно, являются ли и в каком смысле два семантически различны (помимо очевидных различий API, таких как сильная типизация и отсутствие уверенности в строковых ключах).

4b9b3361

Ответ 1

Семантика почти такая же. Оба сохраняются в ExecutionContext и проходят через асинхронные вызовы.

Различия - это изменения API (как описано выше) вместе с возможностью регистрации обратного вызова для изменения значений.

Технически существует большая разница в реализации, поскольку CallContext клонируется каждый раз при копировании (используя CallContext.Clone), в то время как данные AsyncLocal хранятся в словаре ExecutionContext._localValues и только эта ссылка копируется без дополнительной работы.

Чтобы обновления обновлялись только при текущем потоке при изменении значения AsyncLocal, создается новый словарь, а все существующие значения мелко копируются в новый.

Эта разница может быть хорошей и плохой для производительности, в зависимости от того, где используется AsyncLocal.

Теперь, как отметил Ханс Пассант в комментариях, CallContext был первоначально создан для удаленного доступа и недоступен, если удаленное соединение не поддерживается (например,.Net Core), что, вероятно, почему AsyncLocal было добавлено в структуру

#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

Примечание: в SDK Visual Studio также есть AsyncLocal, который в основном представляет собой обертку поверх CallContext, которая показывает, насколько похожи понятия: System.Threading.AsyncLocal.

Ответ 2

Мне интересно, если и каким образом эти два семантически отличаются

Из того, что видно, как CallContext, так и AsyncLocal внутренне ретранслируются на ExecutionContext для хранения своих внутренних данных внутри Dictionary. Последний, похоже, добавляет еще один уровень косвенности для асинхронных вызовов. CallContext был вокруг смысла .NET Remoting и был удобным способом передачи данных между асинхронными вызовами, где до сих пор не было реальной альтернативы.

Самое большое различие, которое я могу обнаружить, заключается в том, что AsyncLocal теперь позволяет вам регистрироваться на уведомления через обратный вызов, когда базовое хранимое значение изменяется либо с помощью переключателя ExecutionContext, либо явно путем замены существующего значения.

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

Кроме того, он находится в System.Threading, а другой живет в System.Runtime.Remoting, где первая будет поддерживаться в CoreCLR.

Кроме того, не кажется, что AsyncLocal имеет неглубокую семантику copy-on-write SetLogicalData, поэтому данные передаются между вызовами без копирования.