我有一个创建任务和继续任务的WebService。
在第一项任务中,我们设置 Thread.CurrentPrincipal
因此,当ContinuationTask启动时,它不再具有Thread.CurrentPrincipal。
我想在 ContinuationTask 中指定应该在与其前提相同的线程中运行。
我搜索了网页,但我发现线程要求在SynchronizationContext中运行,因此我开始认为我缺少一些基本规则,特别是关于Thread.Principal应该如何工作。
答案 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());
复制