Правильное использование Task.Run и Task.WaitAll для вызовов EF в ASP.NET - PullRequest
1 голос
/ 28 мая 2020

КРАТКАЯ ВЕРСИЯ:

Кто-нибудь может подтвердить, дает ли Task.Run/Task.WaitAll для неасинхронных c подфункций с вызовами EF что-нибудь для ASP. NET приложения?

ДЛИННАЯ ВЕРСИЯ:

У нас есть служба Asp. Net WebApi с методом, который выполняет несколько вызовов БД (все с использованием EntityFramework ) для сбора набора данных, которые возвращаются в один объект ответа. Мы разбили это на ряд подфункций и хотели, чтобы они выполнялись параллельно (ПРИМЕЧАНИЕ: ни одна из них не использует никаких асинхронных c вызовов).

Чтобы добиться некоторой формы параллельного кодирования, мы вызываем каждая функция с Task.Run, за которой следует Task.WaitAll:

public ResponseObject PopulateResponseObject(int id)
{
    var response = new ResponseObject();

    Task<DataSetA> dataSetATask = Task.Run(() => getDataSetA(id));
    Task<DataSetB> dataSetBTask = Task.Run(() => getDataSetB(id));
    Task.WaitAll(dataSetATask, dataSetBTask);

    response.SetA = dataSetATask.Result;
    response.SetB = dataSetBTask.Result;

    return response;
}

Из того, что я читал, Task.Run может не сильно выиграть в приложении Asp. Net, и что это может привести к ненужным накладным расходам пула потоков.

Не тратим ли мы время на написание нашего кода таким образом?

ОБНОВЛЕНИЕ: мы знакомы с тем фактом, что EF имеет asyn c версий, но есть значительный объем кода, который необходимо изменить. Мы хотели бы оставить эти функции как есть.

Ответы [ 3 ]

2 голосов
/ 28 мая 2020

Основываясь на моем опыте, я настоятельно рекомендую никогда не убивать простоту ради незначительного прироста производительности. Влияние этого параллелизма кажется мне незначительным с огромной нагрузкой на разработку, сопровождение и отладку. Также предлагаю взглянуть на это .

2 голосов
/ 28 мая 2020

с использованием Task.Run по крайней мере позволяет запускать два запроса параллельно, так что да, можно добиться улучшения (по сравнению с полностью синхронной версией, в которой запросы выполняются последовательно).

Однако выигрыш ограничено количеством потоков в пуле потоков (которое в конечном итоге ограничивается мощностью вашего сервера). Вы получите больший выигрыш, используя API EF asyn c, потому что это позволит выполнять множество запросов без привязки потока для каждого запроса.

Но это разумная стратегия для start с использованием асинхронного c кода. Как только вы это сделаете, вы можете вернуться и медленно преобразовать запросы для использования asyn c api.

0 голосов
/ 29 мая 2020

После тестирования производительности с использованием Syn c, Asyn c (с использованием метода выше) и Parallel.Invoke я обнаружил некоторые интересные результаты. Параллельная версия была похожа на:

public ResponseObject PopulateResponseObject(int id)
{
    var response = new ResponseObject();

    Parallel.Invoke(
        () => response.SetA = getDataSetA(id),
        () => response.SetB = getDataSetB(id),
        );

    return response;
}

И Asyn c появляется выше, в моем исходном сообщении.

Оказывается, в среднем, по сравнению с базовым c Syn c вызовы, вызовы Asyn c (Task.Run/Task.WaitAll) выполнялись в 57% случаев, а Parallel.Invoke выполнялись в 65% случаев.

Таким образом, было преимущество использования Task.Run/WaitAll в приложении ASP. NET.

ОБНОВЛЕНИЕ: Кстати, это тестирование было выполнено путем помещения нашего кода в нашу тестовую среду, где были наши специалисты по контролю качества. выполнение тестов. Это определенно не имитирует c нашу производственную среду, но показывает, что она немного лучше.

...