ASP.NET Core, перебивает Task.Run ()? - PullRequest
0 голосов
/ 04 сентября 2018

Допустим, у нас ядро ​​ASP.NET получает строку в качестве полезной нагрузки, размер порядка пары мегабайт. Первый способ реализации:

[HttpPost("updateinfos")]
public async Task UpdateInfos()
{
    var len = (int)this.Request.ContentLength;
    byte[] b = new byte[len];
    await this.Request.Body.ReadAsync(b,0,len);
    var content = Encoding.UTF8.GetString(b);
    .....
}

Тело читается с ReadAsync, это хорошо, так как у нас есть сокет ввода / вывода в сокете, и асинхронный доступ к нему бесплатен из-за природы самого вызова. Но если присмотреться, метод GetString() - это чисто ЦП, который блокирует с линейной сложностью. В любом случае это как-то влияет на производительность, так как другие клиенты ждут, пока мои байты будут преобразованы в строку. Я думаю, чтобы избежать этого, решение состоит в том, чтобы запустить GetString() в пуле потоков следующим образом:

[HttpPost("updateinfos")]
public async Task UpdateInfos()
{
    var len = (int)this.Request.ContentLength;
    byte[] b = new byte[len];
    await this.Request.Body.ReadAsync(b,0,len);
    var content = await Task.Run(()=>ASCIIEncoding.UTF8.GetString(b));
    .....
}

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

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

1 Ответ

0 голосов
/ 04 сентября 2018

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

Такие вещи, как ASCIIEncoding.UTF8.GetString(b), очень быстрые . Затраты на создание и управление потоком, который инкапсулирует это, намного больше, чем просто выполнение этого непосредственно в том же потоке.

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

В вашем примере это просто не тот случай, поэтому вы должны просто вызывать эти методы синхронно.


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

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

...