Начиная с C # 7.0 асинхронные методы могут возвращать ValueTask <T>. В объяснении говорится, что его следует использовать, когда мы имеем кешированный результат или имитируем асинхронность с помощью синхронного кода. Однако я до сих пор не понимаю, в чем проблема с использованием ValueTask всегда или на самом деле, почему async / await не был создан с типом значения с самого начала. Когда ValueTask не справится с этой задачей?
c#
asynchronous
Stilgar
источник
источник
ValueTask<T>
(с точки зрения распределения) отсутствия материализации для операций, которые на самом деле являются асинхронными (потому что в этом случаеValueTask<T>
все равно потребуется распределение кучи). ТакжеTask<T>
существует проблема с другой поддержкой в библиотеках.Ответы:
Из документации API (выделение добавлено):
источник
Task
выделение (которое в наши дни является небольшим и дешевым), но за счет увеличения существующего выделения вызывающей стороны и удвоения размера возвращаемого значения (влияет на распределение регистров). Хотя это и является очевидным выбором для сценария с буферизованным чтением, применять его по умолчанию ко всем интерфейсам не рекомендуется.Task
иValueTask
может быть использован в качестве синхронного типа возврата (сTask.FromResult
). Но все же есть смысл (хех),ValueTask
если у вас есть что-то, что вы ожидаете быть синхронным.ReadByteAsync
быть классическим примером. Я считаю, что онValueTask
был создан в основном для новых «каналов» (низкоуровневых байтовых потоков), возможно, также используемых в ядре ASP.NET, где производительность действительно имеет значение.Task<T>
по умолчанию. Это только потому, что большинство разработчиков не знакомы с ограничениямиValueTask<T>
(в частности, с правилом «только один раз» и «без блокировки»). Тем не менее, если все разработчики в вашей команде хорошиValueTask<T>
, то я бы порекомендовал руководящие указания на уровне командыValueTask<T>
.Типы структур не являются бесплатными. Копирование структур, размер которых превышает размер ссылки, может быть медленнее, чем копирование ссылки. Хранение структур, превышающих ссылку, занимает больше памяти, чем сохранение ссылки. Структуры, размер которых превышает 64 бита, могут не быть зарегистрированы, когда ссылка может быть зарегистрирована. Преимущества более низкого давления сбора не могут превышать затраты.
К проблемам производительности следует подходить с инженерной дисциплиной. Ставьте цели, измеряйте свой прогресс в сравнении с целями, а затем решайте, как изменить программу, если цели не достигнуты, и оценивать по пути, чтобы убедиться, что ваши изменения действительно улучшаются.
await
был добавлен в C # долгое время после того, какTask<T>
тип уже существует. Было бы несколько извращенно изобретать новый тип, когда он уже существует. Иawait
прошел много итераций дизайна, прежде чем остановиться на той, которая была выпущена в 2012 году. Идеальное - враг хорошего; Лучше поставлять решение, которое хорошо работает с существующей инфраструктурой, а затем, если есть спрос со стороны пользователя, предоставить улучшения позже.Отмечу также, что новая функция, позволяющая использовать предоставляемые пользователем типы в качестве вывода сгенерированного компилятором метода, добавляет значительный риск и нагрузку на тестирование. Когда единственные вещи, которые вы можете вернуть, - это void или задача, команде по тестированию не нужно рассматривать какой-либо сценарий, в котором возвращается какой-то абсолютно сумасшедший тип. Тестирование компилятора означает выяснение не только того, какие программы могут писать люди, но и того, какие программы можно написать, потому что мы хотим, чтобы компилятор компилировал все легальные программы, а не только все разумные программы. Это дорого.
Цель этой вещи - повышение производительности. Это не делает работу, если это не измеримо и значительно улучшает работу. Нет гарантии, что так и будет.
источник
ValueTask<T>
это не подмножествоTask<T>
, это надмножество .Таким образом, он позволяет вам написать один метод, который является либо асинхронным, либо синхронным, вместо того, чтобы писать один метод, идентичный для всех остальных. Вы можете использовать его где угодно,
Task<T>
но часто это ничего не добавляет.Ну, это добавляет одну вещь: это добавляет подразумеваемое обещание вызывающей стороне, что метод фактически использует дополнительные функциональные возможности, которые
ValueTask<T>
предоставляют. Я лично предпочитаю выбирать параметры и возвращаемые типы, которые сообщают вызывающей стороне как можно больше. Не возвращайте,IList<T>
если перечисление не может обеспечить счет; не возвращайся,IEnumerable<T>
если сможешь. Ваши потребители не должны искать какую-либо документацию, чтобы знать, какие из ваших методов могут вызываться синхронно, а какие нет.Я не рассматриваю будущие изменения дизайна как убедительный аргумент. Наоборот: если метод меняет свою семантику, он должен прервать сборку, пока все вызовы к нему не будут обновлены соответствующим образом. Если это считается нежелательным (и, поверьте мне, я сочувствую желанию не нарушать сборку), рассмотрите возможность создания версий интерфейса.
По сути, это то, для чего нужна строгая типизация.
Если некоторые программисты, разрабатывающие асинхронные методы в вашем магазине, не могут принимать обоснованные решения, может быть полезно назначить старшего наставника для каждого из этих менее опытных программистов и проводить еженедельный анализ кода. Если они ошибаются, объясните, почему это должно быть сделано по-другому. Для старших ребят это непосильно, но намного быстрее разогнать юниоров, чем просто бросить их в глубокий конец и дать им какое-то произвольное правило для подражания.
Если парень, который написал метод, не знает, может ли он быть вызван синхронно, то кто на Земле знает ?!
Если у вас так много неопытных программистов, пишущих асинхронные методы, эти же люди их тоже называют? Могут ли они сами определить, какие из них безопасно назвать асинхронными, или же они начнут применять подобное произвольное правило к тому, как они называют эти вещи?
Проблема здесь не в ваших типах возвращаемых данных, а в том, что программистов ставят в роли, к которым они не готовы. Это должно было произойти по какой-то причине, поэтому я уверен, что это не может быть тривиально исправить. Описание этого, конечно, не является решением. Но поиск способа обойти проблему без компилятора также не является решением.
источник
ValueTask<T>
, я предполагаю, что тот, кто написал метод, сделал это, потому что метод фактически используетValueTask<T>
добавленную функциональность . Я не понимаю, почему вы считаете, что все ваши методы должны иметь одинаковый тип возврата. Какова цель там?object
потому что когда-нибудь вы захотите, чтобы они возвращалисьint
вместоstring
.В .Net Core 2.1 есть некоторые изменения . Начиная с .net core 2.1, ValueTask может представлять не только синхронно выполненные действия, но и асинхронное выполнение. Кроме того, мы получаем неуниверсальный
ValueTask
тип.Я оставлю комментарий Стивена Туба , связанный с вашим вопросом:
Функцию можно использовать не только в .net core 2.1. Вы сможете использовать его с пакетом System.Threading.Tasks.Extensions .
источник