简体   繁体   English

在与前一个相同的线程中继续执行任务

[英]Continuation Task in the same thread as previous

I have an WebService that creates a task and a continuation task. 我有一个WebService,它创建一个任务和一个延续任务。

In the first task we set Thread.CurrentPrincipal 在第一个任务中我们设置Thread.CurrentPrincipal

Hence, When the ContinuationTask starts it no longer has the Thread.CurrentPrincipal. 因此,当ContinuationTask启动时,它不再具有Thread.CurrentPrincipal。

I'd like to specify in the ContinuationTask that it should run in the same thread as its antecedent . 我想在ContinuationTask中指定它应该在与其前提相同的线程中运行

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. 我搜索过网络,但我只发现线程要在SynchronizationContext中运行,因此我开始认为我缺少一些基本规则,特别是关于Thread.Principal应该如何工作。

First of all, don't use TaskContinuationOptions.ExecuteSynchronously for this purpose! 首先,不要为此目的使用TaskContinuationOptions.ExecuteSynchronously 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. 总有一些情况它不起作用:太多的递归会导致TPL不能同步执行。 Custom TaskScheduler s are also not obliged to support this. Custom TaskScheduler也没有义务支持这一点。

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 以下是关于该主题的一些解读: 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. 这看起来更直观:我们将MyCompositeTask传递给TPL来运行。 The TPL does not care what we do in our callback. TPL并不关心我们在回调中做了什么。 We can do whatever we want, including calling multiple methods and passing the results. 我们可以做任何我们想做的事情,包括调用多个方法并传递结果。

From my C# textbook (C# 4.0 in a Nutshell): 从我的C#教科书(果壳中的C#4.0):

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. 通过在调用ContinueWith时指定TaskContinuationOptions.ExecuteSynchronously ,可以强制它们[继续任务]在相同的线程[作为它们的前提]上执行:这可以通过减少间接来提高细粒度延续中的性能。

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. 原则上我没有试过这个,但它似乎是你正在寻找的东西,可以与Thread.CurrentPrincipal一起使用。

Here is a link to an MSDN article with some more concrete examples as well 这是一篇MSDN文章的链接,其中还有一些更具体的例子

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 . 尝试将WindowsIdentity对象传递给任务并使用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. using语句确保即使发生异常也会清除模拟身份。

Call the continuation with TaskScheduler.FromCurrentSynchronizationContext(): 使用TaskScheduler.FromCurrentSynchronizationContext()调用continuation:

Task UITask= task.ContinueWith(() =>
{
 this.TextBlock1.Text = "Complete"; 
}, TaskScheduler.FromCurrentSynchronizationContext());

Copied from https://stackoverflow.com/a/4331287/503969 复制自https://stackoverflow.com/a/4331287/503969

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM