C# 需要一个执行上下文来组合异步和TPL代码

C# 需要一个执行上下文来组合异步和TPL代码,c#,asynchronous,task-parallel-library,async-await,executioncontext,C#,Asynchronous,Task Parallel Library,Async Await,Executioncontext,我正在寻找一个执行上下文,它可以很好地与async/await配合使用,同时以以下方式与TPL配合使用(预期行为): (2)很好地使用Task.Run() Context.Store("slot", "value"); await DoSomeAsync(); Assert.AreEqual("value", Context.Read("slot")); Context.Store("slot", "value"); var value = await ReadContext("slot");

我正在寻找一个执行上下文,它可以很好地与
async/await
配合使用,同时以以下方式与TPL配合使用(预期行为):

(2)很好地使用
Task.Run()

Context.Store("slot", "value");
await DoSomeAsync();
Assert.AreEqual("value", Context.Read("slot"));

Context.Store("slot", "value");
var value = await ReadContext("slot");
Assert.AreEqual("value", value);
Context.Store("slot", "value");
var task = Task.Run(() => ReadContext("slot"));
Assert.IsNull(task.Result);
Context.Store("slot", "value");
var value = await Task.Run(() => ReadContext("slot"));
Assert.AreEqual("value", value);
(3)巧妙地处理等待的
任务

Context.Store("slot", "value");
await DoSomeAsync();
Assert.AreEqual("value", Context.Read("slot"));

Context.Store("slot", "value");
var value = await ReadContext("slot");
Assert.AreEqual("value", value);
Context.Store("slot", "value");
var task = Task.Run(() => ReadContext("slot"));
Assert.IsNull(task.Result);
Context.Store("slot", "value");
var value = await Task.Run(() => ReadContext("slot"));
Assert.AreEqual("value", value);
(3)不是必需的,但会很好。我现在使用
CallContext
,但在(2)时失败,因为即使在手动运行的任务中,也可以访问其中存储的值,即使在使用
Task.Factory.StartNew(…,longlunning)
运行的任务中也是如此,后者应强制在单独的线程上运行任务


有没有办法做到这一点?

你真正的问题在于你的评论:

我需要一个地方来存储ASP.NET应用程序中的NHibernate会话。如果我在请求上下文中,HttpContext可以正常工作(并尊重async/await),但一旦我跳转到手动运行的任务中,HttpContext就不可用

首先,您应该避免在ASP.NET应用程序中使用“手动运行任务”;关于这个问题我有一个新的想法

其次,在
HttpContext.Items
中存储东西有点像黑客。在少数情况下,它可能很有用,但IMO管理NHibernate会话是应该在应用程序中正确设计的。这意味着您应该在方法调用中传递会话(或提供对会话访问的服务),或者将其注入到需要它的每个类型中

所以,我真的认为你所寻找的“背景”是一个错误的解决方案。即使这是可能的,但事实并非如此

如前所述,不能同时满足要求(2)和(3)。在
任务.Run
中执行的代码具有访问权限或不具有访问权限;不可能两者都有

正如您所发现的,逻辑调用上下文可以满足要求(1)和(3)(谷歌用户注意:此仅在.NET 4.5下有效,并且仅在存储不可变数据时有效;详细信息)


除非手动删除
任务中代码开头的数据(
FreeNamedDataSlot
),否则无法轻松满足(1)和(2)。请运行
。我认为可能还有另一种解决方案,但它需要在每次等待时定制可等待设备,这完全是麻烦、脆弱和无法维护的。

您真正的问题在于您的评论:

我需要一个地方来存储ASP.NET应用程序中的NHibernate会话。如果我在请求上下文中,HttpContext可以正常工作(并尊重async/await),但一旦我跳转到手动运行的任务中,HttpContext就不可用

首先,您应该避免在ASP.NET应用程序中使用“手动运行任务”;关于这个问题我有一个新的想法

其次,在
HttpContext.Items
中存储东西有点像黑客。在少数情况下,它可能很有用,但IMO管理NHibernate会话是应该在应用程序中正确设计的。这意味着您应该在方法调用中传递会话(或提供对会话访问的服务),或者将其注入到需要它的每个类型中

所以,我真的认为你所寻找的“背景”是一个错误的解决方案。即使这是可能的,但事实并非如此

如前所述,不能同时满足要求(2)和(3)。在
任务.Run
中执行的代码具有访问权限或不具有访问权限;不可能两者都有

正如您所发现的,逻辑调用上下文可以满足要求(1)和(3)(谷歌用户注意:此仅在.NET 4.5下有效,并且仅在存储不可变数据时有效;详细信息)


除非手动删除
任务中代码开头的数据(
FreeNamedDataSlot
),否则无法轻松满足(1)和(2)。请运行
。我认为可能还有另一种解决方案,但它需要在每次等待时定制可等待项,这完全是麻烦、脆弱和无法维护的。

在我看来不像是正常的。你能详细说明一下吗?我不明白(2)处的断言.IsNull(这是阻塞等待任务.Result)。您的意思是类似于
Assert.IsNull(task.Result,“应该为null”)
?基本上,我需要一个位置来存储ASP.NET应用程序中的NHibernate会话<如果我在请求上下文中,code>HttpContext
可以正常工作(并尊重
async/await
),但一旦我跳转到手动运行的任务中,它就不可用了。无论如何,现在我意识到我想要的可能是不可能的,因为当我手动启动任务时,环境不知道我是否要等待它。@Noseratio你是对的,编辑。我很惊讶
CallContext
通过
task传递给池线程。像(2)一样运行
,你确定吗?不管怎么说,现在(2)和(3)的要求在我看来是相互排斥的,如果我没有遗漏任何东西的话。在我看来,这不像是正常的。你能详细说明一下吗?我不明白(2)处的断言.IsNull(这是阻塞等待任务.Result)。您的意思是类似于
Assert.IsNull(task.Result,“应该为null”)
?基本上,我需要一个位置来存储ASP.NET应用程序中的NHibernate会话<如果我在请求上下文中,code>HttpContext
可以正常工作(并尊重
async/await
),但一旦我跳转到手动运行的任务中,它就不可用了。无论如何,现在我意识到我想要的可能是不可能的,因为当我手动启动任务时,环境不知道我是否要等待它。@Noseratio你是对的,编辑。我很惊讶
CallContext
通过
task传递给池线程。像(2)一样运行
,你确定吗?不管怎么说,如果我没有遗漏任何东西的话,(2)和(3)的要求在我看来是相互排斥的。Stephen,我同意从上到下传递会话是最好的解决方案。然而,我知道,你有一个很大的缺点