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

时间:2013-10-16 08:34:29

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

我正在寻找一个执行上下文,它与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)运行的那些应该强制运行的任务中也是如此单独一个线程上的任务。

有没有办法实现这个目标?

1 个答案:

答案 0 :(得分:4)

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

  

我需要一个在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)。

除非您在FreeNamedDataSlot中的代码开头手动删除数据(Task.Run),否则没有简单的方法来满足(1)和(2)。我认为可能还有另一个解决方案,但它需要在每个等待时都进行定制等待,这是完全繁琐,脆弱和不可维护的。