Continuation Task in the same thread as previous
Solution 1
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.
Solution 2
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 callingContinueWith
: 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
Solution 3
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.
Solution 4
Call the continuation with TaskScheduler.FromCurrentSynchronizationContext():
Task UITask= task.ContinueWith(() =>
{
this.TextBlock1.Text = "Complete";
}, TaskScheduler.FromCurrentSynchronizationContext());
Copied from https://stackoverflow.com/a/4331287/503969
Related videos on Youtube
Luis Filipe
Updated on June 21, 2022Comments
-
Luis Filipe almost 2 years
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.
-
Panagiotis Kanavos over 11 yearsTying tasks to threads is a bad idea and error prone in case of exceptions. Instead of fixing the thread's principal and requiring all tasks to use the same thread, try passing the WindowsIDentity or token object to the tasks as state and impersonate the user in each task. Otherwise you run the risk of changing a ThreadPool's identity if an exception occurs and you forget to clear the identity
-
Luis FilipeWe implemented our own IPrincipal and it's the application itself that does the authentication. It seems that the best is to pass the IPrincipal amongst the tasks.
-
-
usr over 11 yearsYou can't force it. It only works with very high probability! There are always cases where it does not work. Voted down because of that.
-
usr over 11 yearsHere's a reference for my previous statement: blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx
-
Ricibob over 8 years"InvalidOperationException was unhandled: The current SynchronizationContext may not be used as a TaskScheduler." :-(