I have an WebService that creates a task and a continuation task.
In the first task we set Thread.CurrentPrincipal
Hence, When the ContinuationTask starts it no longer has the Thread.CurrentPrincipal.
I'd like to specify in the ContinuationTask that it should run in the same thread as its antecedent.
I've searched the web but i only found the requirement for the thread to run in the SynchronizationContext, therefore i am starting to think I am missing some basic rule, specially regarding how Thread.Principal should work.
First of all, don't use TaskContinuationOptions.ExecuteSynchronously
for this purpose! You can't force the continuation on the same thread. It only works with very high probability. There are always cases where it does not work: Too much recursion will cause the TPL not to execute synchronously. Custom TaskScheduler
s are also not obliged to support this.
This is a common misconception, especially because it is being wrongly propagated on the web. Here is some reading on that topic: http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx
If you need to run on the same thread, do this:
Task.Factory.StartNew(() => { First(); Second(); });
So easy.
Let me illustrate why that works by showing an alternative solution:
void MyCompositeTask()
{
var result = First();
Second(result);
}
Task.Factory.StartNew(() => MyCompositeTask());
This looks more intuitive: We pass MyCompositeTask
to the TPL to run. The TPL does not care what we do in our callback. We can do whatever we want, including calling multiple methods and passing the results.
From my C# textbook (C# 4.0 in a Nutshell):
You can force them [continuation tasks] to execute on the same thread [as their antecedent] by specifying TaskContinuationOptions.ExecuteSynchronously
when calling ContinueWith
: this can improve performance in very fine-grained continuations with lessening indirection.
In principal I haven't tried this but it seems to be what you're looking for and could be used in conjunction with Thread.CurrentPrincipal.
Here is a link to an MSDN article with some more concrete examples as well
Setting a pool thread's identity is not a good idea. It ties you to this specific thread and and risks "leaking" the identity in case of exceptions, if you forget to clear the identity in an exception handler. You may end up with unrelated tasks running using the "leaked" identity.
Try passing the WindowsIdentity object to the tasks and impersonate using WindowsIdentity.Impersonate. This will allow you to use any available thread and will safely clear the identity even if an exception occurs.
You can try something like this:
WindowsPrincipal myPrincipal=...;
...
var identity=(WindowsIdentity)myPrincipal.Identity;
var task=Task.Factory.StartNew(ident=>{
var id=(WindowsIdentity)ident;
using(var context=id.Impersonate())
{
//Work using the impersonated identity here
}
return id;
},identity).
.ContinueWith(r=>{
var id = r.Result;
using(var context=id.Impersonate())
{
//Work using the impersonated identity here
}
});
The using
statements ensure that the impersonated identity is cleared even if an exception occurs.
Call the continuation with TaskScheduler.FromCurrentSynchronizationContext():
Task UITask= task.ContinueWith(() =>
{
this.TextBlock1.Text = "Complete";
}, TaskScheduler.FromCurrentSynchronizationContext());
Copied from https://stackoverflow.com/a/4331287/503969