web-dev-qa-db-ja.com

AsyncLocalのセマンティクスは論理呼び出しコンテキストとどのように異なりますか?

.NET 4.6では、AsyncLocal<T>制御の非同期フローに沿って周囲データを流すためのクラス。以前に使用したCallContext.LogicalGet/SetDataこの目的のために、私は2つが意味的に異なるかどうか、どのように意味があるのか​​疑問に思っています(厳密な型指定や文字列キーへの依存の欠如などの明らかなAPIの違いを超えて)。

56
ChaseMedallion

セマンティクスはほとんど同じです。両方ともExecutionContextに保存され、非同期呼び出しを介して流れます。

違いは、APIの変更(説明したとおり)と、値の変更のためにコールバックを登録する機能です。

技術的には、CallContextはコピーされるたびに複製されるため、実装には大きな違いがあります(CallContext.CloneAsyncLocalのデータはExecutionContext._localValues辞書とその参照だけが余分な作業なしでコピーされます。

AsyncLocalの値を変更したときに更新が現在のフローにのみ影響するように、新しい辞書が作成され、既存の値はすべて新しいものに浅くコピーされます。

AsyncLocalが使用される場所に応じて、その違いはパフォーマンスにとって良いことも悪いこともあります。

現在、Hans Passantがコメントで言及したように、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

注:Visual Studio SDKには、基本的にAsyncLocalのラッパーであるCallContextもあり、概念がどのように似ているかを示します: Microsoft.VisualStudio.Threading

45
i3arnon

私は2つが意味的に異なるかどうか、どのような方法で疑問に思っています

見ることができることから、CallContextAsyncLocalの両方は、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が浅いコピーオンライトセマンティクスSetLogicalDataを持っているとは思われないため、データはコピーされずに呼び出し間を流れます。

20
Yuval Itzchakov

タイミングに意味的な違いがあるように見えます。

CallContextでは、子スレッド/タスク/非同期メソッドのコンテキストが設定されたとき、つまりTask.Factory.StartNew()、Task.Run()またはasyncメソッドが呼び出されたときに、コンテキストの変更が発生します。

AsyncLocalでは、子スレッド/タスク/非同期メソッドが実際に実行を開始すると、コンテキストの変更(呼び出される変更通知コールバック)が発生します。

特に、コンテキストが切り替えられたときにコンテキストオブジェクトを複製する場合は、タイミングの違いが興味深い場合があります。異なるメカニズムを使用すると、異なるコンテンツが複製される可能性があります。CallContextを使用すると、子スレッド/タスクの作成時または非同期メソッドの呼び出し時にコンテンツを複製できます。ただし、AsyncLocalを使用すると、子スレッド/タスク/非同期メソッドが実行を開始したときにコンテンツを複製します。コンテキストオブジェクトのコンテンツは、親スレッドによって変更されている可能性があります。

7
WenningQiu