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

时间:2012-12-27 16:18:44

标签: c# task-parallel-library impersonation

我有一个创建任务和继续任务的WebService。

在第一项任务中,我们设置 Thread.CurrentPrincipal

因此,当ContinuationTask启动时,它不再具有Thread.CurrentPrincipal。

我想在 ContinuationTask 中指定应该在与其前提相同的线程中运行。

我搜索了网页,但我发现线程要求在SynchronizationContext中运行,因此我开始认为我缺少一些基本规则,特别是关于Thread.Principal应该如何工作。

4 个答案:

答案 0 :(得分:15)

首先,不要将TaskContinuationOptions.ExecuteSynchronously用于此目的!您不能强制在同一个线程上继续。它只能以非常高的概率运行。总有一些情况它不起作用:太多的递归会导致TPL不能同步执行。自定义TaskScheduler也没有义务支持这一点。

这是一种常见的误解,特别是因为它在网络上被错误地传播。以下是对该主题的一些解读:http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx

如果您需要在同一个线程上运行,请执行以下操作:

Task.Factory.StartNew(() => { First(); Second(); });

太容易了。

让我通过展示替代解决方案来说明原因:

void MyCompositeTask()
{
  var result = First();
  Second(result);
}
Task.Factory.StartNew(() => MyCompositeTask());

这看起来更直观:我们将MyCompositeTask传递给TPL来运行。 TPL并不关心我们在回调中做了什么。我们可以随心所欲,包括调用多种方法并传递结果。

答案 1 :(得分:4)

从我的C#教科书(坚果壳中的C#4.0):

  

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

原则上我没试过这个,但它似乎是你正在寻找的东西,可以和Thread.CurrentPrincipal一起使用。

以下是MSDN article的链接以及更具体的示例

答案 2 :(得分:3)

设置池线程的身份不是一个好主意。它将您绑定到此特定线程,如果您忘记在异常处理程序中清除标识,则可能会在异常情况下“泄漏”标识。您最终可能会使用“泄露”身份运行不相关的任务。

尝试将WindowsIdentity对象传递给任务并使用WindowsIdentity.Impersonate进行模拟。这将允许您使用任何可用的线程,并且即使发生异常也将安全地清除身份。

您可以尝试这样的事情:

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
        }
});

using语句确保即使发生异常也会清除模拟身份。

答案 3 :(得分:3)

使用TaskScheduler.FromCurrentSynchronizationContext()调用continuation:

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

https://stackoverflow.com/a/4331287/503969

复制