我已使用此处https://blogs.msdn.microsoft.com/pfxteam/2011/01/15/asynclazyt/中所述的通用异步缓存实现。该解决方案的缺点是Task.Factory.StartNew
(以及Task.Run
)在线程池上创建了新线程,而原始Web请求对此一无所知,因此可能导致启动新线程和线程池饥饿。那么,是否可以进行任何修改,以便将该类同时用于桌面和Web应用程序?对于使用HostingEnvironment.QueueBackgroundWorkItem
而不是Task.Run
/ Task.Factory.StartNew
的Web请求。感谢您的帮助/想法。
private static async Task<T> GetTask(Func<T> valueFactory)
{
if (HostingEnvironment.IsHosted)
{
T value = default(T);
HostingEnvironment.QueueBackgroundWorkItem((_) => value = valueFactory());
await Task.Delay(1).ConfigureAwait(false);
return value;
}
else
{
return Task.Factory.StartNew(valueFactory).Result;
}
}
public AsyncLazy(Func<T> valueFactory) : base(() => GetTask(valueFactory))
{
}
答案 0 :(得分:2)
首先,我想强调一点,您要尝试做的可能是个坏主意。 HostingEnvironment.QueueBackgroundWorkItem
将不阻止创建线程或限制线程池饥饿。它实际上是在后台的线程池上运行您的工作。其目的只是跟踪当前正在运行的后台任务,以便ASP.NET可以在回收AppDomain之前等待它们完成(例如,当您在web.config文件中进行更改时,可能会发生)。
另外,要使用HostingEnvironment
,您需要对System.Web.dll
的引用,因此,它不是您应该用于控制台应用程序的帮助程序。
也就是说,这确实是您想要执行的操作。代码中的第一个问题是您正在调用.Result
。 这将导致线程池饥饿。相反,请等待任务(此外,除非有充分的理由要使用Task.Run
,否则应始终使用Task.Factory.StartNew
。如果您不知道这些原因是什么,则这是您应一定改为使用Task.Run
。
第二个问题是:如何等待在HostingEnvironment上安排的项目?为此,您应该使用TaskCompletionSource
。您的代码将如下所示:
private static Task<T> GetTask<T>(Func<T> valueFactory)
{
if (HostingEnvironment.IsHosted)
{
var tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
HostingEnvironment.QueueBackgroundWorkItem(_ =>
{
tcs.SetResult(valueFactory());
});
return tcs.Task;
}
return Task.Run(valueFactory);
}
答案 1 :(得分:0)
TaskFactory
具有接受TaskScheduler
(TaskFactory(CancellationToken, TaskCreationOptions, TaskContinuationOptions, TaskScheduler)
和TaskFactory(TaskScheduler)
)的构造函数。
TaskScheduler
类还充当所有可定制调度逻辑的扩展点。
覆盖抽象方法QueueTask(Task)
,TryDequeue(Task)
和TryExecuteTaskInline(Task, Boolean)
以转发到HostingEnvironment.QueueBackgroundWorkItem(Func<CancellationToken, Task>)
在Web应用程序中,您将TaskFactory
与自己的TaskScheduler
一起使用,在控制台应用程序中,您将使用默认的TaskFactory
。在这种情况下,您有充分的理由使用TaskFactory.StartNew
而不是Task.Run
。