【问题标题】:Need an execution context for combination of async and TPL code需要一个执行上下文来组合异步和 TPL 代码
【发布时间】:2013-10-16 08:34:29
【问题描述】:

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

async Task<string> ReadContext(string slot)
{
    // Perform some async code
    ...
    return Context.Read(slot);
}

(1) 与async/await玩得很好

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

(2) 与Task.Run()玩得很好

Context.Store("slot", "value");
var task = Task.Run(() => ReadContext("slot"));
Assert.IsNull(task.Result);

(3) 与期待已久的Task 玩得很好

Context.Store("slot", "value");
var value = await Task.Run(() => ReadContext("slot"));
Assert.AreEqual("value", value);

(3) 不是必需的,但会很好。我现在使用CallContext,但它在 (2) 处失败,因为即使在手动运行的任务中也可以访问存储在其中的值,即使在使用 Task.Factory.StartNew(..., LongRunning) 运行的任务中也应该强制在单独的线程。

有没有办法做到这一点?

【问题讨论】:

  • 在我看来不像一个普通的Execution Ctx。你能详细说明一下吗?
  • 我不明白 (2) 处的Assert.IsNull(这是对 task.Result 的阻塞等待)。你的意思是像Assert.IsNull(task.Result, "should be null") 这样的东西吗?
  • 基本上,我需要一个地方来在 ASP.NET 应用程序中存储 NHibernate 会话。如果我在请求上下文中,HttpContext 可以正常工作(并尊重async/await),但是一旦我跳入手动运行的任务,它就无法使用。无论如何,现在我意识到我想要的可能是不可能的,因为当我手动启动任务时,环境不知道我是否要等待它。
  • @Noseratio 你说得对,已编辑。
  • 我很惊讶CallContext(2) 一样通过 Task.Run 传递给池线程,你确定吗?无论如何,如果我没有遗漏任何东西,现在对 (2)(3) 的要求对我来说看起来是相互排斥的。

标签: c# asynchronous task-parallel-library async-await executioncontext


【解决方案1】:

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

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

首先,您应该完全避免在 ASP.NET 应用程序中“手动运行任务”;我有一个关于这个主题的blog post

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

所以,我真的认为像您正在寻找的“上下文”是一个错误的解决方案。即使有可能,但事实并非如此。

正如@Noseratio 所指出的,要求 (2) 和 (3) 不能同时满足。在Task.Run 中执行的代码要么有访问权限,要么没有;不可能两者兼有。

正如您所发现的,逻辑调用上下文可以满足要求 (1) 和 (3)(Google 员工注意:这仅在 .NET 4.5 下有效并且仅当您存储不可变数据;详情on my blog)。

没有简单的方法可以满足 (1) 和 (2),除非您手动删除 Task.Run 中代码开头的数据 (FreeNamedDataSlot)。我认为可能还有另一种解决方案,但它需要在每次等待时自定义等待对象,这完全是麻烦、脆弱和不可维护的。

【讨论】:

  • 斯蒂芬,我同意从顶部一直向下传递会话是最好的解决方案。但是,恕我直言,您必须在任何地方使用这种方法有一个很大的缺点。你不能在不提出某种背景的情况下稍微“放松”这个要求。
  • 至于根本不手动运行任务 - 如果操作需要很长时间处理并且HTTP请求无法等待它完成(我认为这通常是一个好习惯),我必须这样做)。是的,我读过your article :-)
猜你喜欢
  • 1970-01-01
  • 2018-05-06
  • 2017-09-25
  • 2013-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多