2015-06-16 4 views
1

В asp.net Web API, как можно асинхронно вызывать длительные блокирующие действия?Асинхронные вызовы в Web Api

У меня есть действие контроллера веб-api, которое должно сделать относительно (10 секунд, как пример) длительный вызов БД. Это похоже на кандидата для асинхронного метода. Я могу разгрузить долго выполняющиеся задачи на новую нить, и разблокирование моего запроса asp.net потока для обработки некоторых других запросов, так что мой упрощена действие контроллера будет выглядеть следующим образом:

public async Task<IHttpActionResult> Get() 
{ 
    IEnumerable<Thing> things = await Task.Run(() => DoLongDbCall()); 
    return Ok(things); 
} 

Я сталкивался парой блог (this one, например), которые предполагают, что это не может быть оптимальным способом достижения этого в asp.net. Автор предлагает использовать Task.FromResult() и выполнять вызов БД синхронно, но я не вижу, как это помогает; Моя очередь запросов по-прежнему будет заблокирована, ожидая возврата вызова БД.

+0

Все запросы уже выполняются в отдельном потоке, поэтому вам не нужно блокировать, если вы не хотите выполнять два или более вызова параллельно. Вызов 'await Task.Run' просто * отнимает * другой поток, когда вы можете использовать исходный поток. –

+1

Вы понимаете, что в этом сценарии использование 'async' /' await' действительно не дает вам ничего? – James

+1

ADO.NET * и * EF * do * поддерживают асинхронные вызовы. 'DoLongDbCall' должен сам быть асинхронным методом. В этом случае вы будете освобождать текущий поток, ожидая базы данных * без *, используя дополнительный поток, чтобы ждать –

ответ

1

моя очередь запроса по-прежнему будет заблокирована, ожидая возврата вызова БД.

Это не совсем верно:

Ваш запрос будет еще ждать длительной эксплуатации до конца, но ваш поток будет свободно обрабатывать другие операции.

Вы должны помнить, что IIS имеет уменьшенное количество доступных потоков (особенно при работе в несерверных системах) и освобождение того, что не нужно, всегда хорошо.

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

+0

Итак, какова наилучшая реализация вышеуказанного кода? – alisabzevari

4

Во-первых, рассмотрим, что происходит с синхронным вызовом:

public IHttpActionResult Get() 
{ 
    IEnumerable<Thing> things = DoLongDbCall(); 
    return Ok(things); 
} 

приходит запрос, и ASP.NET захватывает поток пула потоков для обработки запроса. Этот поток вызывает метод Get, который выполняет эту работу. Один поток потока потока используется во время всего запроса.

Теперь давайте разберем, что происходит в текущем коде (с помощью Task.Run):

public async Task<IHttpActionResult> Get() 
{ 
    IEnumerable<Thing> things = await Task.Run(() => DoLongDbCall()); 
    return Ok(things); 
} 

приходит запрос, и ASP.NET захватывает поток пула потоков для обработки запроса. Этот поток вызывает метод Get, который затем захватывает другой поток потока потоков для выполнения работы и возвращает исходный поток пула назад в пул потоков. Один поток потока потока используется во время всего запроса (и два потока пула потоков используются в течение очень короткого периода времени).

Итак, код Task.Run заставляет дополнительный переход на потоки без каких-либо преимуществ (вся цель использования async на стороне сервера - освободить темы). Вот почему я рекомендую не использовать Task.Run (или любой другой способ запустить работу с пулом потоков) на ASP.NET.

Надлежащее асинхронное решение будет использовать асинхронный DB звонки:

public async Task<IHttpActionResult> Get() 
{ 
    IEnumerable<Thing> things = await DoLongDbCallAsync(); 
    return Ok(things); 
} 

приходит запрос, и ASP.NET захватывает поток пул потоков для обработки запроса. Этот поток вызывает метод Get, который затем запускает асинхронную операцию и возвращает поток пула потоков обратно в пул потоков. Позже, когда вызов db завершается, ASP.NET захватывает поток пула потоков для завершения запроса. Для большей части запроса используются no потоки пула потоков (один поток потока потока используется в течение короткого периода времени в начале и в конце запроса).

+0

Это имеет смысл до некоторой степени, но я не вижу разницы между 'Task.Run (() => LongAction) и LongActionAsync. Независимо от того, сколько слоев абстракции я наложу поверх нее, моя «асинхронность» потребует создания нового потока в какой-то момент? – richzilla

+1

@richzilla: Нет, это не обязательно требует отдельного потока. Я объясняю [как «async' может быть без threadless] (http://blog.stephencleary.com/2013/11/there-is-no-thread.html) в моем блоге. –

Смежные вопросы