There are two questions here. The first is whether or not Thread.CurrentPrincipal
is propagated to threads in PLINQ.
The answer is yes, as this is part of the ExecutionContext
and the ExecutionContext is captured from the calling thread and copied to the new/recycled thread when a new thread/task/thread pool thread is started.
The following test case (run in .NET 4.0) shows this:
[TestMethod]
public void TestMethod1()
{
// Capture the current logged in account.
// Could be a GenericPrincipal as well with some random value
// set on the identity name.
IPrincipal p = new WindowsPrincipal(WindowsIdentity.GetCurrent());
// Set the current principal.
Thread.CurrentPrincipal = p;
// Set the synchronization context.
SynchronizationContext.SetSynchronizationContext(
new SynchronizationContext());
// Context is not null.
Assert.IsNotNull(SynchronizationContext.Current);
// PLINQ.
var plinqThreadDetails =
// Go parallel. This number needs to be reasonably
// high to force parallelization as PLINQ might
// use this thread if the size is small.
from i in Enumerable.Range(0, 4000).AsParallel().
// Force parallelization. At best, this is
// a suggestion.
WithExecutionMode(ParallelExecutionMode.ForceParallelism)
select new {
// These values are retreived on another thread.
IdentityName = Thread.CurrentPrincipal.Identity.Name,
Thread.CurrentThread.ManagedThreadId,
};
// Was there any parallelization?
bool anyParallel = false;
// Make assertions.
// The managed thread id is different than the current one.
foreach (var plinqThreadDetail in plinqThreadDetails)
{
// But the principal still flowed, even though on a different
// thread.
Assert.AreEqual(Thread.CurrentPrincipal.Identity.Name,
plinqThreadDetail.IdentityName);
// Update any parallel.
anyParallel |= (plinqThreadDetail.ManagedThreadId !=
Thread.CurrentThread.ManagedThreadId);
}
// There was *some* parallelization.
Assert.IsTrue(anyParallel);
}
Regarding whether or not SynchronizationContext
is used in PLINQ, it's not, and it doesn't make sense to.
Considering that using a SynchronizationContext
usually means serializing a call to a particular context (which is usually a thread, think UI applications, but not always, given the ASP.NET synchronization context), you'd kill any gains that PLINQ would gain from parallelization because every call would have to be marshaled back through the SynchronizationContext
.
The benefits in PLINQ come from being able to execute these operations at the same time, not one-at-a-time.
The following test case (very much along the lines of the previous one) proves that the SynchronizationContext
is not captured for PLINQ threads:
[TestMethod]
public void TestMethod2()
{
// Set the synchronization context.
SynchronizationContext.SetSynchronizationContext(
new SynchronizationContext());
// Context is not null.
Assert.IsNotNull(SynchronizationContext.Current);
// PLINQ.
var plinqThreadDetails =
// Go parallel. This number needs to be reasonably
// high to force parallelization as PLINQ might
// use this thread if the size is small.
from i in Enumerable.Range(0, 4000).AsParallel().
// Force parallelization.
WithExecutionMode(ParallelExecutionMode.ForceParallelism)
select new {
// These values are retreived on another thread.
SynchronizationContextIsNull =
SynchronizationContext.Current == null,
Thread.CurrentThread.ManagedThreadId,
};
// Make assertions.
// Was there any parallelization?
bool anyParallel = false;
// Make assertions.
// The synchronization context on the PLINQ thread was
// not set, only if on a different thread.
foreach (var plinqThreadDetail in plinqThreadDetails)
{
// If the thread id is different.
if (plinqThreadDetail.ManagedThreadId !=
Thread.CurrentThread.ManagedThreadId)
{
// The synchronization context was null.
Assert.IsTrue(plinqThreadDetail.SynchronizationContextIsNull);
// There was something on another thread.
anyParallel = true;
}
else
{
// The synchronization context is not null.
Assert.IsFalse(plinqThreadDetail.SynchronizationContextIsNull);
}
}
// There was *some* parallelization.
Assert.IsTrue(anyParallel);
}
ExecutionContext
yourself - I'm just saying that it's theExecutionContext
that's important here, not theSynchronizationContext
. (It's probably really a context withinExecutionContext
, butSynchronizationContext
is all about what thread code runs on, not who it runs as.) – JohanajohananAspNetSynchronizationContext
does flow thread's cultures and they are part ofExecutionContext
. And so isCurrentPrincipal
, right? – TunstallThreadPool
or it needs to create a new one. Btw, did you try my test on 4000 input? – TunstallForceParllelism
is a suggestion at best. The 4000 case fails as well. Remember, the issue is whether or not the principal is brought over to other threads. I've updated the answer to reflect the test we should be running to reflect that, and to determine whether or not any parallelization took place (and to only succeed when there is some parallelization). – KeeseCurrentPrincipal
I need to perform additional testing in my code. Perhaps hosting environment makes the difference. Thanks for your efforts! – Tunstall