Web请求的AsyncLazy实现

时间:2018-12-21 09:22:06

标签: c# async-await

我已使用此处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))
{
}

2 个答案:

答案 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具有接受TaskSchedulerTaskFactory(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