Looking for c# Keywords? Try Ask4Keywords

C# Language ASP.NET Configure Await


пример

Когда ASP.NET обрабатывает запрос, поток создается из пула потоков и создается контекст запроса . Контекст запроса содержит информацию о текущем запросе, к которому можно получить доступ через статическое свойство HttpContext.Current . Затем контекст запроса для запроса присваивается потоку, обрабатывающему запрос.

Конкретный контекст запроса может быть активен только по одному потоку за раз .

Когда выполнение достигает await , поток, обрабатывающий запрос, возвращается в пул потоков, пока выполняется асинхронный метод, и контекст запроса свободен для использования другого потока.

public async Task<ActionResult> Index()
{
    // Execution on the initially assigned thread
    var products = await dbContext.Products.ToListAsync();

    // Execution resumes on a "random" thread from the pool
    // Execution continues using the original request context.
    return View(products);
}

Когда задача завершается, пул потоков назначает другой поток для продолжения выполнения запроса. Затем контекст запроса присваивается этому потоку. Это может быть или не быть исходной нитью.

блокировка

Когда результат вызова метода async синхронно, могут возникнуть взаимоблокировки. Например, следующий код приведет к взаимоблокировке при IndexSync() :

public async Task<ActionResult> Index()
{
    // Execution on the initially assigned thread
    List<Product> products = await dbContext.Products.ToListAsync();

    // Execution resumes on a "random" thread from the pool
    return View(products);
}

public ActionResult IndexSync()
{
    Task<ActionResult> task = Index();

    // Block waiting for the result synchronously
    ActionResult result = Task.Result;

    return result;       
}

Это связано с тем, что по умолчанию ожидаемая задача, в этом случае db.Products.ToListAsync() захватит контекст (в случае ASP.NET контекст запроса) и попытается использовать его после его завершения.

Когда весь стек вызовов асинхронен, проблем нет, потому что, как только await достигнуто, исходный поток освобождается, освобождая контекст запроса.

Когда мы блокируем синхронное использование Task.Result или Task.Wait() (или других методов блокировки), исходный поток все еще активен и сохраняет контекст запроса. Ожидаемый метод все еще работает асинхронно, и как только обратный вызов пытается выполнить, т. Е. Как только ожидаемая задача вернулась, он пытается получить контекст запроса.

Поэтому тупик возникает из-за того, что, пока блокирующий поток с контекстом запроса ожидает завершения асинхронной операции, асинхронная операция пытается получить контекст запроса для завершения.

ConfigureAwait

По умолчанию вызовы ожидаемой задачи будут захватывать текущий контекст и пытаться возобновить выполнение в контексте после завершения.

Используя ConfigureAwait(false) это может быть предотвращено, а взаимоблокировки можно избежать.

public async Task<ActionResult> Index()
{
    // Execution on the initially assigned thread
    List<Product> products = await dbContext.Products.ToListAsync().ConfigureAwait(false);

    // Execution resumes on a "random" thread from the pool without the original request context
    return View(products);
}

public ActionResult IndexSync()
{
    Task<ActionResult> task = Index();

    // Block waiting for the result synchronously
    ActionResult result = Task.Result;

    return result;       
}

Это позволяет избежать блокировок, когда необходимо блокировать асинхронный код, однако это происходит за счет потери контекста в продолжении (код после ожидания вызова).

В ASP.NET это означает, что если ваш код после вызова await someTask.ConfigureAwait(false); пытается получить доступ к информации из контекста, например HttpContext.Current.User тогда информация была потеряна. В этом случае HttpContext.Current имеет значение NULL. Например:

public async Task<ActionResult> Index()
{
    // Contains information about the user sending the request
    var user = System.Web.HttpContext.Current.User;

    using (var client = new HttpClient())
    {
        await client.GetAsync("http://google.com").ConfigureAwait(false);
    }

    // Null Reference Exception, Current is null
    var user2 = System.Web.HttpContext.Current.User;

    return View();
}

Если ConfigureAwait(true) используется (эквивалентно отсутствию ConfigureAwait), то как user и user2 заполняются теми же данными.

По этой причине часто рекомендуется использовать ConfigureAwait(false) в библиотечном коде, где контекст больше не используется.