Continuation Task in the same thread as previous

11,168

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 TaskSchedulers 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 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

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

Share:
11,168

Related videos on Youtube

Luis Filipe
Author by

Luis Filipe

Updated on June 21, 2022

Comments

  • Luis Filipe
    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
      Panagiotis Kanavos over 11 years
      Tying 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 Filipe
      Luis Filipe
      We 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
    usr over 11 years
    You 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
    usr over 11 years
    Here's a reference for my previous statement: blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx
  • Ricibob
    Ricibob over 8 years
    "InvalidOperationException was unhandled: The current SynchronizationContext may not be used as a TaskScheduler." :-(