在我的 ASP.NET MVC 应用程序中,我需要实现/覆盖一些内部实体,例如 IModelBinder.BindModel()
, IActionFilter.OnActionExecuting()
等。这些方法正在调用异步方法,但显然我不能将它们变成 async Task<>
能够使用 await
关键字。
所以我写了下面的“适配器”:
public static T GetResult<T>(Func<Task<T>> func)
{
var httpContext = HttpContext.Current;
var proxyTask = Task.Run(() =>
{
HttpContext.Current = httpContext;
return func();
});
return proxyTask.Result;
}
这让我调用我的异步 GetData()
以同步方式:
public static async Task<Data> GetData()
{
if (isCached)
return GetCachedData();
var data = await GetOriginalData();
SetCachedData(data);
return data;
}
...
var data = GetResult(() => GetData());
好的,它有效,所以我会结束它,但是如您所见,大多数时候,GetData()
同步运行,因此无条件生成新线程在性能方面并不好。这让我很烦恼,所以我最终得到了一个不同的解决方案:
public static T GetResult<T>(Func<Task<T>> func)
{
var syncContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
var task = func();
SynchronizationContext.SetSynchronizationContext(syncContext);
return task.Result;
}
它也有效,但问题是自 SynchronizationContext
不再流入,没有HttpContext
在被调用的方法中可用。
我可能可以通过输入 HttpContext.Current
来解决这个问题进入逻辑 CallContext
但我仍然想知道是否有更好的方法来解决这个问题?像定制 SynchronizationContext.Current.CreateCopy()
.
最佳答案
spawning a new thread unconditionally
其实就是简单的从线程池中借用一个线程。几乎没有开始一个新线程那么糟糕,但仍然不是最好的性能。
It also works, however the issue is that since SynchronizationContext is no longer flowing in, there is no HttpContext available in the called method.
你确定吗?我希望 HttpContext.Current
设置在 GetData
的开头,以便两种方式调用它。我还希望 HttpContext.Current
在 await
之后为 null
以两种方式调用它。如果在您的测试中 await
之后它不是 null
,可能是因为延续恰好在同一个线程上结束。
这是尝试在请求上下文 (SynchronizationContext
) 之外使用 HttpContext.Current
的主要问题之一。当您将它放在一个裸线程 (Task.Run
) 上时,它会一直呆在那里,直到该线程进入另一个请求上下文。
I could probably work that around by putting HttpContext.Current into logical CallContext
我建议在原始请求上下文中从 Current
中提取您需要的任何数据,然后将该数据显式传递给需要它的方法。
关于原始问题(避免同步情况下的额外线程),我建议只添加一个同步 TryGetData
方法。
关于c# - 如何使 HttpContext 在同步调用的任务中可用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41331644/