Передается ли идентификатор потока при использовании расширений PLINQ? - PullRequest
8 голосов
/ 30 ноября 2011

Я использую .AsParallel (). ForAll () для параллельного перечисления коллекции в контексте запроса ASP.NET. Метод перечисления опирается на System.Threading.Thread.CurrentPrincipal.

Могу ли я полагаться на отдельные потоки, в которых для их System.Threading.Thread.CurrentPrincipal установлено значение HttpContext.Current.User потока, обрабатывающего запрос ASP.NET, или мне нужно управлять этим самостоятельно?

Другой способ задать вопрос - наследуют ли потоки, используемые PLINQ, идентификатор потока, который вызвал операцию?

Ответы [ 3 ]

11 голосов
/ 05 декабря 2011

Нет, идентификатор не будет автоматически распространяться на эти рабочие потоки.Если, на самом деле, вы используете 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;
    }
});
3 голосов
/ 30 ноября 2011

Это реализация за 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 и глобализация

1 голос
/ 04 февраля 2015

Одна тонкая вещь, которую следует заметить при прохождении Принципал через .AsParallel () border: Где ваша последовательность материализуется?

Это не проблема с .ForAll () , но рассмотрим другой сценарий:

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

Затем вы передаете результат в другом месте, так что он пересекает границу потока (что, вероятно, скажем, если вы возвращаете его из метода действия WCF).

В этом случае ко времени применения MyTransform , Thread. CurrentPrincipal может содержать неожиданное значение.

Итак, обходной путь здесь заключается в том, чтобы материализовать запрос на месте (вызывая .ToArray () , .ToList () и т. Д.)

...