8

.AsParallel()。ForAll()を使用して、ASP.NET要求のコンテキストでコレクションを並列に列挙しています。列挙メソッドは、System.Threading.Thread.CurrentPrincipalに依存しています。

System.Threading.Thread.CurrentPrincipalをASP.NET要求を処理しているスレッドのHttpContext.Current.Userに設定するために使用される個々のスレッドに依存できますか、それとも自分で管理する必要がありますか?

質問をする別の方法は、PLINQによって使用されるスレッドが、操作を呼び出したスレッドのIDを継承するかどうかです。

4

3 に答える 3

11

いいえ、IDはこれらのワーカースレッドに自動的に伝播されません。実際、使用しているコンポーネントがHttpContext.User実行可能な場合は、「メイン」スレッド内の現在の「アンビエント」HttpContextインスタンスをキャプチャして、ワーカースレッドに伝播します。これは次のようになります。

HttpContext currentHttpContext = HttpContext.Current;

myWorkItems.AsParallel().ForAll(wi =>
{ 
    HttpContext.Current = currentHttpContext;

    try
    {
        // anything called from here out will find/use the context of your original ASP.NET thread
    }
    finally
    {
       // Disassociate the context from the worker thread so that it is not held on to beyond its official lifetime
       HttpContext.Current = null;
    }
});

これHttpContext.Currentは、静的スレッドによってサポートされているため機能します。したがって、すべてのワーカースレッドにはメインスレッドからインスタンスが割り当てられ、その時点から実行されたすべての作業で、それが現在のインスタンスとして認識されます。

ここで、とそれに関連するクラスはスレッドセーフになるように設計されていないことに注意する必要がHttpContextあります。したがって、これはちょっとしたハックです。プロパティから読み取るだけの場合、これは実際には問題ではありません。に依存するコンポーネントを使用していない場合はHttpContext.Current、それを設定せずに、キャプチャされたcurrentHttpContext変数をワーカーで直接使用する方が「クリーン」です。

最後に、本当に必要なのが現在のプリンシパルをワーカースレッドに伝達することだけである場合は、同じアプローチを使用する代わりに、それを行うことができます。

Principal logicalPrincipal = Thread.CurrentPrincipal;

myWorkItems.AsParallel().ForAll(wi =>
{ 
    Principal originalWorkerThreadPrincipal = Thread.CurrentPrincipal;
    Thread.CurrentPrincipal = logicalPrincipal;

    try
    {
        // anything called from here out will find the principal from your original thread
    }
    finally
    {
       // Revert to the original identity when work is complete
       Thread.CurrentPrincipal = originalWorkerThreadPrincipal;
    }
});
于 2011-12-04T20:38:39.893 に答える
3

これは背後にある実装ですCurrentPrincipal

public static IPrincipal CurrentPrincipal
{
    get
    {
        lock (CurrentThread)
        {
            IPrincipal threadPrincipal = CallContext.Principal;
            if (threadPrincipal == null)
            {
                threadPrincipal = GetDomain().GetThreadPrincipal();
                CallContext.Principal = threadPrincipal;
            }
            return threadPrincipal;
        }
    }
    set { CallContext.Principal = value; }
}

新しく作成されたすべてのスレッドはnullになり、アプリケーションドメインから取得されます。だからそれは大丈夫なはずです。それにもかかわらず、あなたは文化に注意する必要があります。開始スレッドから派生することはありません。参照:並列プログラミング、PLINQおよびグローバリゼーション

于 2011-11-30T09:33:23.787 に答える
1

プリンシパル.AsParallel()境界を通過するときに注意すべき微妙な点が1つあります。それは、シーケンスが具体化される場所です。

これは.ForAll()の問題ではありませんが、別のシナリオを検討してください。

var result = items.AsParallel().Select(MyTransform);

次に、結果を他の場所に渡して、スレッドの境界を越えるようにします(たとえば、WCFアクションメソッドから結果を返す場合)。

この場合、MyTransformが適用されるまでに、スレッドになります。CurrentPrincipal値には、予期しないものが含まれている可能性があります。

したがって、ここでの回避策は、その場でクエリを具体化することです(.ToArray()、. ToList()などを呼び出すことによって)

于 2015-02-04T13:42:14.683 に答える