C# 为什么是“一个”呢;等待任务。屈服();Thread.CurrentPrincipal是否需要正确流动?
下面的代码已添加到新创建的Visual Studio 2012.NET 4.5 WebAPI项目中 我试图以异步方法分配C# 为什么是“一个”呢;等待任务。屈服();Thread.CurrentPrincipal是否需要正确流动?,c#,asp.net-web-api,async-await,C#,Asp.net Web Api,Async Await,下面的代码已添加到新创建的Visual Studio 2012.NET 4.5 WebAPI项目中 我试图以异步方法分配HttpContext.Current.User和Thread.CurrentPrincipal。除非等待任务.Yield(),否则线程.CurrentPrincipal的赋值不正确(或任何其他异步操作)(将true传递到authenticateSync()将导致成功) 为什么呢 using System.Security.Principal; using System.Thr
HttpContext.Current.User
和Thread.CurrentPrincipal
。除非等待任务.Yield(),否则线程.CurrentPrincipal
的赋值不正确执行代码>(或任何其他异步操作)(将true
传递到authenticateSync()
将导致成功)
为什么呢
using System.Security.Principal;
using System.Threading.Tasks;
using System.Web.Http;
namespace ExampleWebApi.Controllers
{
public class ValuesController : ApiController
{
public async Task GetAsync()
{
await AuthenticateAsync(false);
if (!(User is MyPrincipal))
{
throw new System.Exception("User is incorrect type.");
}
}
private static async Task AuthenticateAsync(bool yield)
{
if (yield)
{
// Why is this required?
await Task.Yield();
}
var principal = new MyPrincipal();
System.Web.HttpContext.Current.User = principal;
System.Threading.Thread.CurrentPrincipal = principal;
}
class MyPrincipal : GenericPrincipal
{
public MyPrincipal()
: base(new GenericIdentity("<name>"), new string[] {})
{
}
}
}
}
使用System.Security.Principal;
使用System.Threading.Tasks;
使用System.Web.Http;
命名空间ExampleWebApi.Controllers
{
公共类值控制器:ApiController
{
公共异步任务GetAsync()
{
等待验证同步(false);
如果(!(用户是我的委托人))
{
抛出新的System.Exception(“用户类型不正确”);
}
}
私有静态异步任务同步(bool-yield)
{
如果(收益率)
{
//为什么需要这样做?
等待任务;
}
var principal=新的MyPrincipal();
System.Web.HttpContext.Current.User=主体;
System.Threading.Thread.CurrentPrincipal=主体;
}
类MyPrincipal:GenericPrincipal
{
公共机构负责人()
:base(新GenericEntity(“”),新字符串[]{})
{
}
}
}
}
注:
等待任务.Yield()代码>可以出现在authenticateSync()
中的任何位置,也可以在调用authenticateSync()
后移动到GetAsync()
中,并且仍然会成功
ApiController.User
返回Thread.CurrentPrincipal
HttpContext.Current.User
始终正确流动,即使没有wait Task.Yield()
Web.config
包括
,它使用TaskFriendlySynchronizationContext
- 几天前我问过,但没有意识到这个例子之所以成功,是因为存在
Task.Delay(1000)
多么有趣!似乎Thread.CurrentPrincipal
基于逻辑调用上下文,而不是每线程调用上下文。在我看来,这是非常不直观的,我很想知道为什么它是以这种方式实现的
在.NET4.5.中,async
方法与逻辑调用上下文交互,使其更适合于async
方法。我有一个朋友;那是唯一有记录的地方。在.NET4.5中,在每个async
方法的开头,它为其逻辑调用上下文激活“写时复制”行为。当(如果)修改逻辑调用上下文时,它将首先创建自身的本地副本
通过观察观察观察窗口中的System.Threading.Thread.CurrentThread.ExecutionContextBelongsToCurrentScope
,可以查看逻辑调用上下文的“本地性”(即,是否已复制)
如果您没有产生,那么当您设置Thread.CurrentPrincipal
时,您正在创建逻辑调用上下文的副本,该副本被视为该异步方法的“本地”。当async
方法返回时,本地上下文将被丢弃,原始上下文将取代它(您可以看到ExecutionContextBelongsToCurrentScope
返回到false
)
另一方面,如果您做了屈服
,那么同步上下文
行为将接管。实际发生的是捕获HttpContext
,并用于恢复这两种方法。在本例中,您没有看到从authenticateSync
到GetAsync
保存的Thread.CurrentPrincipal
;实际情况是在方法恢复之前
如果将Yield
移动到GetAsync
,您会看到类似的行为:线程。CurrentPrincipal
被视为一个本地修改,作用域为AuthenticateTasync
;当该方法返回时,它将恢复其值。但是,HttpContext.User
仍然设置正确,并且该值将被Yield
捕获,当方法恢复时,它将覆盖线程。CurrentPrincipal如果您忽略它,会发生什么?@SLaks,如果等待任务。Yield()
被跳过,Thread.CurrentPrincipal
恢复到调用wait authenticateSync()之前的状态。由于Thread.CurrentPrincipal
不再是MyPrincipal
,因此引发了异常。在我的Owin中间件中,我必须加入最后一块中间件,它只是等待Task.Yield();这似乎会导致Thread.CurrentPrincipal在整个执行过程中与预期一致。您好!你听说过为什么要这样做吗?这篇文章我已经读了三遍了,但仍然让我神魂颠倒。@vtortola:我不确定。我假设是这样,用户权限将自动流向后台线程。这可能是十年前的事了,而更新行为的复制上下文则是最近才出现的。所以他们以这种奇怪的方式发生了冲突。