Реализация AsyncLazy для веб-запросов - PullRequest
0 голосов
/ 21 декабря 2018

Я использовал общую реализацию асинхронного кэша, описанную здесь https://blogs.msdn.microsoft.com/pfxteam/2011/01/15/asynclazyt/. Недостатком этого решения является то, что Task.Factory.StartNew (а также Task.Run) создает новые потоки в пуле потоков без каких-либо знаний об этом исходного веб-запроса.Это может привести к запуску новых потоков и истощению потоков.Так есть ли какие-либо возможные модификации, чтобы класс мог использоваться как для настольных, так и для веб-приложений?Для веб-запроса используйте HostingEnvironment.QueueBackgroundWorkItem вместо Task.Run / Task.Factory.StartNew.Спасибо за любую помощь / идеи.

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 голосов
/ 22 декабря 2018

TaskFactory имеет конструкторы, которые принимают TaskScheduler (TaskFactory(CancellationToken, TaskCreationOptions, TaskContinuationOptions, TaskScheduler) и TaskFactory(TaskScheduler)).

Класс TaskScheduler также служит точкой расширения для всей настраиваемой логики планирования.

Переопределить абстрактные методы QueueTask(Task), TryDequeue(Task) и TryExecuteTaskInline(Task, Boolean) для пересылки на HostingEnvironment.QueueBackgroundWorkItem(Func<CancellationToken, Task>)

В веб-приложении вы используете TaskFactory с вашим собственным TaskScheduler в консольном приложении вы используете значение по умолчанию TaskFactory.И в этом случае у вас есть веские основания использовать TaskFactory.StartNew вместо Task.Run.

0 голосов
/ 21 декабря 2018

Во-первых, я хотел бы подчеркнуть, что то, что вы пытаетесь сделать, вероятно, плохая идея.HostingEnvironment.QueueBackgroundWorkItem будет не предотвращать создание потоков или ограничивать истощение пула потоков.Это на самом деле запускает вашу работу в пуле за кулисами.Его цель - просто отслеживать фоновые задачи, которые в данный момент выполняются, чтобы ASP.NET мог дождаться их завершения, прежде чем перезапускать домен приложения (что может произойти, например, при внесении изменений в файл 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...