<?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE article PUBLIC "-//NLM/DTD JATS (Z39.96) Journal Publishing DTD v1.2 20120330//EN" "http://jats.nlm.nih.gov/publishing/1.2/JATS-journalpublishing1.dtd">
    <!--<?xml-stylesheet type="text/xsl" href="article.xsl">-->
<article xmlns:ns0="http://www.w3.org/1999/xlink" xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" article-type="research-article" dtd-version="1.2" xml:lang="en">
	<front>
		<journal-meta>
			<journal-id journal-id-type="issn">2303-9868</journal-id>
			<journal-id journal-id-type="eissn">2227-6017</journal-id>
			<journal-title-group>
				<journal-title>Международный научно-исследовательский журнал</journal-title>
			</journal-title-group>
			<issn pub-type="epub">2303-9868</issn>
			<publisher>
				<publisher-name>ООО Цифра</publisher-name>
			</publisher>
		</journal-meta>
		<article-meta>
			<article-id pub-id-type="doi">10.60797/IRJ.2025.157.40</article-id>
			<article-categories>
				<subj-group>
					<subject>Brief communication</subject>
				</subj-group>
			</article-categories>
			<title-group>
				<article-title>Оптимизация асинхронных операций в .NET</article-title>
			</title-group>
			<contrib-group>
				<contrib contrib-type="author" corresp="yes">
					<contrib-id contrib-id-type="orcid">https://orcid.org/0000-0001-9359-911X</contrib-id>
					<contrib-id contrib-id-type="rinc">https://elibrary.ru/author_profile.asp?id=830879</contrib-id>
					<contrib-id contrib-id-type="rid">https://publons.com/researcher/GLN-3042-2022</contrib-id>
					<name>
						<surname>Гибадуллин</surname>
						<given-names>Руслан Фаршатович</given-names>
					</name>
					<email>landwatersun@mail.ru</email>
					<xref ref-type="aff" rid="aff-2">2</xref>
				</contrib>
				<contrib contrib-type="author">
					<name>
						<surname>Гашигуллин</surname>
						<given-names>Данил Айратович</given-names>
					</name>
					<email>gashigullin44@gmail.com</email>
					<xref ref-type="aff" rid="aff-1">1</xref>
				</contrib>
			</contrib-group>
			<aff id="aff-1">
				<label>1</label>
				<institution>Казанский национальный исследовательский технический университет им. А.Н. Туполева – КАИ</institution>
			</aff>
			<aff id="aff-2">
				<label>2</label>
				<institution>Казанский национальный исследовательский технический университет им. А.Н. Туполева – КАИ</institution>
			</aff>
			<pub-date publication-format="electronic" date-type="pub" iso-8601-date="2025-07-17">
				<day>17</day>
				<month>07</month>
				<year>2025</year>
			</pub-date>
			<pub-date pub-type="collection">
				<year>2025</year>
			</pub-date>
			<volume>15</volume>
			<issue>157</issue>
			<fpage>1</fpage>
			<lpage>15</lpage>
			<history>
				<date date-type="received" iso-8601-date="2025-04-28">
					<day>28</day>
					<month>04</month>
					<year>2025</year>
				</date>
				<date date-type="accepted" iso-8601-date="2025-06-06">
					<day>06</day>
					<month>06</month>
					<year>2025</year>
				</date>
			</history>
			<permissions>
				<copyright-statement>Copyright: &amp;#x00A9; 2022 The Author(s)</copyright-statement>
				<copyright-year>2022</copyright-year>
				<license license-type="open-access" xlink:href="http://creativecommons.org/licenses/by/4.0/">
					<license-p>
						This is an open-access article distributed under the terms of the Creative Commons Attribution 4.0 International License (CC-BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited. See 
						<uri xlink:href="http://creativecommons.org/licenses/by/4.0/">http://creativecommons.org/licenses/by/4.0/</uri>
					</license-p>
					.
				</license>
			</permissions>
			<self-uri xlink:href="https://research-journal.org/archive/7-157-2025-july/10.60797/IRJ.2025.157.40"/>
			<abstract>
				<p>Статья посвящена принципам управления состояниями асинхронных операций на платформе .NET с целью минимизации аллокаций в управляемой памяти. Проведён анализ типичных сценариев использования асинхронного программирования в клиент-серверных приложениях. Основное внимание уделено следующим аспектам: синхронное завершение асинхронных функций, стратегии кеширования задач Task и Task&lt;T&gt;, применение типов ValueTask и ValueTask&lt;T&gt;, реализации интерфейса IValueTaskSource&lt;T&gt;. Приведены результаты тестирования различных типов асинхронных методов на примере вычисления функции Аккермана. Статья предназначена для разработчиков программного обеспечения, работающих над созданием высокопроизводительных и отзывчивых .NET-приложений, в которых критически важна оптимизация ресурсоёмких асинхронных операций.</p>
			</abstract>
			<kwd-group>
				<kwd>платформа .NET</kwd>
				<kwd> асинхронное программирование</kwd>
				<kwd> параллельное программирование</kwd>
				<kwd> Task</kwd>
				<kwd> ValueTask</kwd>
				<kwd> IValueTaskSource</kwd>
				<kwd> кеширование задач</kwd>
				<kwd> синхронное завершение</kwd>
				<kwd> оптимизация памяти</kwd>
				<kwd> производительность</kwd>
			</kwd-group>
		</article-meta>
	</front>
	<body>
		<sec>
			<title>HTML-content</title>
			<p>1. Введение</p>
			<p>Принцип асинхронного программирования состоит в том, что длительно выполняющиеся (или потенциально длительно выполняющиеся) функции реализуются асинхронным образом. Он отличается от традиционного подхода синхронной реализации длительно выполняющихся функций с последующим их вызовом в новом потоке или в задаче для введения параллелизма по мере необходимости.</p>
			<p>Асинхронный подход обеспечивает:</p>
			<p>- параллельное выполнение операций ввода-вывода без связывания потоков;</p>
			<p>- уменьшение количества кода в рабочих потоках обогащенных клиентских приложений (созвучно с понятием «</p>
			<p>Это приводит к двум сценариям использования асинхронного программирования.</p>
			<p>Первый сценарий касается серверных приложений, которые обрабатывают множество параллельных операций ввода-вывода. В таких приложениях важна не безопасность потоков (разделяемое состояние минимально), а эффективность их использования, чтобы поток, обрабатывающий клиентские запросы, не простаивал на сетевых операциях.</p>
			<p>Второй сценарий упрощает поддержку потокобезопасности в обогащенных клиентских приложениях, для которых в целях упрощения программы проводится рефакторинг крупных методов в методы меньших размеров, получая в результате цепочки методов, которые вызывают друг друга (графы вызовов).</p>
			<p>Если любая операция в графе синхронных вызовов длительная, то весь граф запускается в рабочем потоке (или рабочих потоках) для сохранения отзывчивости пользовательского интерфейса. Так реализуется крупномодульный параллелизм.</p>
			<p>Мелкомодульный параллелизм — последовательность небольших параллельных операций, между которыми выполнение возвращается в главный поток пользовательского интерфейса [1]. Например, методы, отвечающие за отражение промежуточных результатов, не требуют значительного времени и могут выполняться в основном потоке, что упрощает потокобезопасность.</p>
			<p>Целесообразность применения асинхронного программирования несомненна при работе с операциями ввода-вывода и трудоемкими по времени вычислениями </p>
			<p>[2][3]</p>
			<p>Целью исследования является разработка принципов асинхронного программирования, обеспечивающих эффективное управление состояниями асинхронных операций при снижении накладных расходов на управление памятью. Основное внимание уделяется следующим аспектам:</p>
			<p>- синхронное завершение асинхронной функции;</p>
			<p>- кеширование </p>
			<p>- применение структуры </p>
			<p>- реализация IValueTaskSource&lt;T&gt;.</p>
			<p>2. Синхронное
завершение асинхронной
функции</p>
			<p>Возврат из асинхронной функции может произойти перед организацией ожидания. Рассмотрим следующий код, который обеспечивает кеширование в процессе загрузки веб-страниц:</p>
			<p>При ожидании задачи компилятор оптимизирует код, проверяя свойство IsCompleted. Если задача уже завершена (например, при наличии данных в кеше), выполнение происходит с возвратом завершённого экземпляра задачи без создания продолжения. Это называется синхронным завершением. В противном случае создается продолжение для асинхронного выполнения. Такой подход позволяет избежать накладных расходов на асинхронность, когда она не требуется, ускоряя выполнение кода, когда данные доступны немедленно.</p>
			<p>В представленном примере ожидание асинхронной функции, которая завершается синхронно, все равно связано с небольшими накладными расходами (компилятор все равно добавляет код для управления состоянием метода и возможным продолжением) — примерно 20 наносекунд на современных компьютерах. Напротив, переход в пул потоков вызывает переключение контекста — возможно одну или две микросекунды, а переход в цикл обработки сообщений пользовательского интерфейса — минимум в десять раз больше (и еще больше, если пользовательский интерфейс занят) [3].</p>
			<p>Асинхронное программирование в C# предоставляет интересную возможность: создавать асинхронные методы, которые фактически никогда не используют ключевое слово await. Например, можно написать метод вида:</p>
			<p>Хотя компилятор и выдаст предупреждение об отсутствии await, такой код вполне допустим и работоспособен. Альтернативный способ достижения того же результата заключается в использовании метода Task.FromResult, который возвращает уже завершенную задачу. В этом случае метод может выглядеть так:</p>
			<p>Этот вариант не требует использования ключевого слова async. Оба подхода позволяют сохранить согласованность интерфейса и обеспечивают гибкость при реализации асинхронных интерфейсов. Они особенно полезны, когда необходимо работать с асинхронным кодом в преимущественно синхронных сценариях. Важно отметить, что в обоих случаях возвращается сигнализированная (завершенная) задача. Когда же метод помечен как async и использует await, компилятор автоматически генерирует состояние машины (state machine) для управления асинхронными операциями. Это включает в себя создание нескольких объектов и структур для управления жизненным циклом задачи, что влечет за собой дополнительные накладные расходы.</p>
			<p>Если наш метод </p>
			<p>Существует простой способ достичь указанной цели, не прибегая к блокировкам или сигнализирующим конструкциям. Вместо кеша строк мы создаем объект-обещания (</p>
			<p>Обратите внимание, что мы не помечаем метод как </p>
			<p>Теперь при повторяющихся вызовах метода </p>
			<p>[4][5]</p>
			<p>Чтобы сделать код безопасным к потокам без защиты со стороны контекста синхронизации, необходимо блокировать все тело метода </p>
			<p>[6]</p>
			<p>В данном решении мы производим блокировку не на время загрузки страницы (это нанесло бы ущерб параллелизму), а на небольшой промежуток времени, пока проверяется кеш и при необходимости запускается новая задача, которая обновляет кеш.</p>
			<p>3. Кеширование
Task</p>
			<p>Одной из ключевых концепций в асинхронном программировании является использование класса </p>
			<p>[3]</p>
			<p>Среда выполнения .NET предоставляет механизмы для снижения числа создаваемых объектов в управляемой памяти. Когда метод завершается синхронно, нет необходимости создавать новый объект </p>
			<p>[7]</p>
			<p>Для иллюстрации рассмотрим следующий пример:</p>
			<p>В данном примере, если метод завершается синхронно, ему не нужно возвращать новый </p>
			<p>Таким образом, если в методе </p>
			<p>Или, например, представим код, в котором метод </p>
			<fig id="F1">
				<label>Figure 1</label>
				<caption>
					<p>Вывод программы при setAsync = false</p>
				</caption>
				<alt-text>Вывод программы при setAsync = false</alt-text>
				<graphic ns0:href="/media/images/2025-06-27/4122a7d2-70e5-4b76-b592-a4a4be2c04c1.png"/>
			</fig>
			<fig id="F2">
				<label>Figure 2</label>
				<caption>
					<p>Вывод программы при setAsync = true</p>
				</caption>
				<alt-text>Вывод программы при setAsync = true</alt-text>
				<graphic ns0:href="/media/images/2025-06-27/ed32d4fd-6e8a-4715-bc8d-39be665d1329.png"/>
			</fig>
			<p>Поскольку есть только два возможных результата типа bool(true и false), то существует только два возможных объекта Task, которые нужны для представления этих результатов. В сценарии синхронного завершения среда .NET обеспечивает кеширование этих объектов, возвращая их с соответствующим значением без выделения памяти. Только в случае асинхронного завершения (достигается вызовом RandomWithAsyncDelay с параметром setAsync равным true) методу понадобится создать новый Task, потому что его нужно будет вернуть до того, как станет известен результат операции. Вывод программы при различных значениях параметра setAsync представлен на рисунках 1 и 2.[8]</p>
			<p>Множество методов библиотеки стремятся сгладить ситуацию за счет использования собственного кеша. В частности, в .NET Framework 4.5 метод MemoryStream.ReadAsync всегда выполняется синхронно, поскольку он считывает данные из памяти (данный метод можно найти в исходном коде .NET на GitHub [9]).</p>
			<p>4. Применение
структуры ValueTask</p>
			<p>.NET Core 2.0 ввел новый тип </p>
			<p>[10][11]</p>
			<p>В этом примере, если </p>
			<p>Однако при разработке высокопроизводительных сервисов по-прежнему важно минимизировать любое выделение памяти, включая размещения в куче объектов, которые связаны с асинхронными операциями. В .NET Core 2.1 тип </p>
			<p>[12][13]</p>
			<p>Основные методы </p>
			<p>- </p>
			<p>- </p>
			<p>- </p>
			<p>Интерфейс </p>
			<p>Проблема повторного использования </p>
			<p>Вывод программы:</p>
			<p>firstValueTask.Result: Результат #1</p>
			<p>firstValueTask.Result: Результат #2</p>
			<p>secondValueTask.Result: Результат #2</p>
			<p>Структурно представим выполнение вышеуказанного кода:</p>
			<p>1. Первый запрос к базе данных:</p>
			<p>1.1. Создается объект </p>
			<p>1.2. Запускаются два потока, которые ожидают завершения этой операции. Первый поток немедленно выполняет </p>
			<p>1.3. Второй поток ожидает завершения </p>
			<p>2. Второй запрос к базе данных:</p>
			<p>2.1. Тот же объект </p>
			<p>2.2. Запускается еще один поток, который асинхронно ожидает завершения второй операции.</p>
			<p>Таким образом, второй поток, который ожидает завершения </p>
			<p>В представленном коде закомментированная строка:</p>
			<p>objPool.token = (</p>
			<p>shortEnvironment</p>
			<p>играет важную роль в предотвращении проблемы повторного использования экземпляра </p>
			<p>Таким образом, возможность повторного использования экземпляра </p>
			<p>Когда </p>
			<p>Однако с развитием технологий и появлением требования исключить выделение памяти даже при асинхронных завершениях операций, необходимость в необобщенном </p>
			<p>5. Реализация IValueTaskSource c применением ManualResetValueTaskSourceCore</p>
			<p>Реализация интерфейса </p>
			<p>[14]</p>
			<p>С введением </p>
			<p>На рисунке 3 представлен вывод после запуска кода:</p>
			<fig id="F3">
				<label>Figure 3</label>
				<caption>
					<p>Вывод программы с использованием ManualResetValueTaskSourceCore</p>
				</caption>
				<alt-text>Вывод программы с использованием ManualResetValueTaskSourceCore</alt-text>
				<graphic ns0:href="/media/images/2025-06-27/dd8b7bc4-7a78-4c6a-8e19-70cbf37461f2.png"/>
			</fig>
			<p>Сброс (Reset) очищает все внутренние состояния объекта, такие как захваченные контексты выполнения, продолжения, исключения и результат предыдущей операции. После вызова _core.Reset поле _version будет инкрементировано и свойство _core.Version вернет значение, которое не будет соответствовать первоначальному. Это важно для предотвращения ошибок при многократном ожидании завершения одной и той же задачи. В приведенном выше коде объект из пула был возвращен и повторно использован слишком рано, в то время, когда один из потоков все еще ожидал первоначальный объект.</p>
			<p>6. Ограничения
на использование ValueTask</p>
			<p>Рассмотрим ограничения, нарушение которых может привести к некорректной работе программы, состояниям гонки и другим труднодиагностируемым ошибкам.</p>
			<p>Первое – многократное ожидание (</p>
			<p>Второе — использование метода </p>
			<p>Указанные ограничения определяют важное правило: с экземплярами </p>
			<p>Из-за представленных ограничений, требующих соблюдения определенных рекомендаций, программисты реже прибегают к использованию </p>
			<p>[15][16]</p>
			<p>Функция Аккермана обладает высокой степенью рекурсивной вложенности. В представленном коде в конечной точке рекурсии происходит синхронное завершение, в результате весь стек вызовов тоже разворачивается синхронно, и выполнение не уходит в асинхронность. В этом случае </p>
			<p>Результаты тестирования реализаций функции Аккермана с применением </p>
			<p>[17]</p>
			<p>-    процессор Intel Core i5-9300H (8 логических, 4 физических ядра)</p>
			<p>-    оперативная память DDR4 16 ГБ,</p>
			<p>-    операционная система Windows 11 (10.0.22631.4460),</p>
			<p>Runtime=.NET 9.0.3 (9.0.325.11113), X64 RyuJIT AVX2.</p>
			<table-wrap id="T1">
				<label>Table 1</label>
				<caption>
					<p>Результаты тестирования реализаций функции Аккермана</p>
				</caption>
				<table>
					<tr>
						<td>Метод</td>
						<td>Число рек.  вызовов</td>
						<td>m</td>
						<td>n</td>
						<td>Средн. арифм., нс.</td>
						<td>Станд. откл., нс.</td>
						<td>Кол-во сборок мусора в Gen0 на 1000 операций</td>
						<td>Кол-во выделяемой памяти в куче за 1 вызов метода, байт</td>
					</tr>
					<tr>
						<td>Baseline</td>
						<td>4</td>
						<td>1</td>
						<td>1</td>
						<td>3,506</td>
						<td>0,0902</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>ValueTask</td>
						<td>76,036</td>
						<td>2,2531</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>IValueTaskSource</td>
						<td>378,316</td>
						<td>11,3795</td>
						<td>0,0877</td>
						<td>368</td>
					</tr>
					<tr>
						<td>Task</td>
						<td>50,700</td>
						<td>1,0317</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>Baseline</td>
						<td>6</td>
						<td>1</td>
						<td>2</td>
						<td>7,313</td>
						<td>0,0782</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>ValueTask</td>
						<td>106,601</td>
						<td>6,3150</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>IValueTaskSource</td>
						<td>503,147</td>
						<td>28,4584</td>
						<td>0,1354</td>
						<td>568</td>
					</tr>
					<tr>
						<td>Task</td>
						<td>65,809</td>
						<td>2,1491</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>Baseline</td>
						<td>8</td>
						<td>1</td>
						<td>3</td>
						<td>8,576</td>
						<td>0,2664</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>ValueTask</td>
						<td>136,545</td>
						<td>3,2220</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>IValueTaskSource</td>
						<td>678,346</td>
						<td>19,3167</td>
						<td>0,1831</td>
						<td>768</td>
					</tr>
					<tr>
						<td>Task</td>
						<td>89,820</td>
						<td>2,4432</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>Baseline</td>
						<td>14</td>
						<td>2</td>
						<td>1</td>
						<td>18,519</td>
						<td>0,6279</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>ValueTask</td>
						<td>270,178</td>
						<td>7,9779</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>IValueTaskSource</td>
						<td>1221,201</td>
						<td>29,1151</td>
						<td>0,3242</td>
						<td>1360</td>
					</tr>
					<tr>
						<td>Task</td>
						<td>163,981</td>
						<td>5,7236</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>Baseline</td>
						<td>27</td>
						<td>2</td>
						<td>2</td>
						<td>36,497</td>
						<td>0,6551</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>ValueTask</td>
						<td>482,350</td>
						<td>11,3347</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>IValueTaskSource</td>
						<td>3009,284</td>
						<td>60,8933</td>
						<td>0,6599</td>
						<td>3376</td>
					</tr>
					<tr>
						<td>Task</td>
						<td>352,306</td>
						<td>7,8184</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>Baseline</td>
						<td>44</td>
						<td>2</td>
						<td>3</td>
						<td>57,001</td>
						<td>0,7175</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>ValueTask</td>
						<td>814,358</td>
						<td>28,9962</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>IValueTaskSource</td>
						<td>4343,942</td>
						<td>172,8679</td>
						<td>1,5030</td>
						<td>6296</td>
					</tr>
					<tr>
						<td>Task</td>
						<td>569,717</td>
						<td>13,4053</td>
						<td>0,0515</td>
						<td>216</td>
					</tr>
					<tr>
						<td>Baseline</td>
						<td>106</td>
						<td>3</td>
						<td>1</td>
						<td>135,360</td>
						<td>1,1921</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>ValueTask</td>
						<td>1961,375</td>
						<td>62,9385</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>IValueTaskSource</td>
						<td>10705,974</td>
						<td>446,4759</td>
						<td>4,0436</td>
						<td>16937</td>
					</tr>
					<tr>
						<td>Task</td>
						<td>1534,464</td>
						<td>40,6479</td>
						<td>0,3777</td>
						<td>1584</td>
					</tr>
					<tr>
						<td>Baseline</td>
						<td>541</td>
						<td>3</td>
						<td>2</td>
						<td>674,350</td>
						<td>5,9826</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>ValueTask</td>
						<td>9459,547</td>
						<td>181,8086</td>
						<td> </td>
						<td> </td>
					</tr>
					<tr>
						<td>IValueTaskSource</td>
						<td>66247,500</td>
						<td>5013,7841</td>
						<td>21,8506</td>
						<td>91701</td>
					</tr>
					<tr>
						<td>Task</td>
						<td>9222,128</td>
						<td>378,4115</td>
						<td>4,7455</td>
						<td>19872</td>
					</tr>
					<tr>
						<td>Baseline</td>
						<td>2432</td>
						<td>3</td>
						<td>3</td>
						<td>3293,043</td>
						<td>44,9792</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>ValueTask</td>
						<td>44116,709</td>
						<td>1783,5449</td>
						<td>-</td>
						<td>-</td>
					</tr>
					<tr>
						<td>IValueTaskSource</td>
						<td>414282,242</td>
						<td>17464,7995</td>
						<td>96,6797</td>
						<td>416848</td>
					</tr>
					<tr>
						<td>Task</td>
						<td>46579,047</td>
						<td>1897,1332</td>
						<td>30,3345</td>
						<td>126864</td>
					</tr>
				</table>
			</table-wrap>
			<fig id="F4">
				<label>Figure 4</label>
				<caption>
					<p>Времена работы методов Baseline, ValueTask, IValueTaskSource, Task</p>
				</caption>
				<alt-text>Времена работы методов Baseline, ValueTask, IValueTaskSource, Task</alt-text>
				<graphic ns0:href="/media/images/2025-06-27/0668c766-c164-464f-97d0-becdd4c52733.png"/>
			</fig>
			<fig id="F5">
				<label>Figure 5</label>
				<caption>
					<p>Количество сборок мусора в ходе работы методов Task и IValueTaskSource</p>
				</caption>
				<alt-text>Количество сборок мусора в ходе работы методов Task и IValueTaskSource</alt-text>
				<graphic ns0:href="/media/images/2025-06-27/d00b3253-1fb6-4fc6-8d24-f1eec1bb08e4.png"/>
			</fig>
			<p>- При передаче экземпляра структуры </p>
			<p>- </p>
			<p>Адаптация функции Аккермана к интерфейсу IValueTaskSource&lt;T&gt; не дало выигрыша ни в производительности, ни в использовании управляемой памяти, так как использование глубокой рекурсии приводит к переполнению пула объектов.</p>
			<p>7. Заключение</p>
			<p>Асинхронное программирование в .NET представляет собой мощный инструмент для повышения производительности и отзывчивости приложений, особенно в сценариях, связанных с операциями ввода-вывода или длительными вычислениями.</p>
			<p>По итогам проведенного анализа сформированы принципы оптимизации асинхронных операций:</p>
			<p>- Выполнение предварительной проверки завершения асинхронной операции и, если результат уже доступен (например, из кеша), обеспечить синхронное завершение метода, чтобы избежать затрат на создание асинхронной инфраструктуры. Использование кешированных задач, таких как </p>
			<p>- Применение методов, возвращающих </p>
			<p>- Использование структуры </p>
			<p>- Реализация механизма управления асинхронными операциями через интерфейс </p>
			<p>Результаты тестирования в синхронном сценарии на примере функции Аккермана показали, что при малых аллокациях в памяти целесообразно применение Task&lt;T&gt;, так как данный тип не уступает в производительности ValueTask&lt;T&gt;. В асинхронном сценарии с возвращаемой структурой ValueTask&lt;TResult&gt; положительный результат потенциально достижим с применением интерфейса IValueTaskSource&lt;T&gt; и эффективным использованием пула объектов (т.е. не вызывая его переполнения).</p>
		</sec>
		<sec sec-type="supplementary-material">
			<title>Additional File</title>
			<p>The additional file for this article can be found as follows:</p>
			<supplementary-material xmlns:xlink="http://www.w3.org/1999/xlink" id="S1" xlink:href="https://doi.org/10.5334/cpsy.78.s1">
				<!--[<inline-supplementary-material xlink:title="local_file" xlink:href="https://research-journal.org/media/articles/19642.docx">19642.docx</inline-supplementary-material>]-->
				<!--[<inline-supplementary-material xlink:title="local_file" xlink:href="https://research-journal.org/media/articles/19642.pdf">19642.pdf</inline-supplementary-material>]-->
				<label>Online Supplementary Material</label>
				<caption>
					<p>
						Further description of analytic pipeline and patient demographic information. DOI:
						<italic>
							<uri>https://doi.org/10.60797/IRJ.2025.157.40</uri>
						</italic>
					</p>
				</caption>
			</supplementary-material>
		</sec>
	</body>
	<back>
		<ack>
			<title>Acknowledgements</title>
			<p/>
		</ack>
		<sec>
			<title>Competing Interests</title>
			<p/>
		</sec>
		<ref-list>
			<ref id="B1">
				<label>1</label>
				<mixed-citation publication-type="confproc">Гибадуллин Р.Ф. Потокобезопасные вызовы элементов управления в обогащенных клиентских приложениях / Р.Ф. Гибадуллин // Программные системы и вычислительные методы. — 2022. — № 4. — С. 1–19. — DOI: 10.7256/2454-0714.2022.4.39029.</mixed-citation>
			</ref>
			<ref id="B2">
				<label>2</label>
				<mixed-citation publication-type="confproc">Damyanov D. Using Asynchronous Programming in C# — Problems, Practical Tips and Scenarios: A Short Review / D. Damyanov, Z. Varbanov // 2024 5th International Conference on Communications, Information, Electronic and Energy Systems (CIEES). — Veliko Tarnovo, 2024. — P. 1–6. — DOI: 10.1109/CIEES62939.2024.10811272.</mixed-citation>
			</ref>
			<ref id="B3">
				<label>3</label>
				<mixed-citation publication-type="confproc">Albahari J. C# 12 in a Nutshell: The Definitive Reference / J. Albahari. — O'Reilly Media, 2023.</mixed-citation>
			</ref>
			<ref id="B4">
				<label>4</label>
				<mixed-citation publication-type="confproc">Прозорова А.П. Влияние на производительность приложения малого объема памяти и использование Garbage Collector (GC) / А.П. Прозорова, Е.В. Вершинин, А.Е. Потапов // Электронный журнал: наука, техника и образование. — 2019. — № 1. — С. 55–61.</mixed-citation>
			</ref>
			<ref id="B5">
				<label>5</label>
				<mixed-citation publication-type="confproc">Williams T. Effective .NET Memory Management: Build memory-efficient cross-platform applications using .NET Core / T. Williams. — Packt Publishing, 2024.</mixed-citation>
			</ref>
			<ref id="B6">
				<label>6</label>
				<mixed-citation publication-type="confproc">Гибадуллин Р.Ф. Неоднозначность результатов при использовании методов класса Parallel в рамках исполняющей среды. NET Framework / Р.Ф. Гибадуллин, И.В. Викторов // Программные системы и вычислительные методы. — 2023. — № 2. — С. 1–14.</mixed-citation>
			</ref>
			<ref id="B7">
				<label>7</label>
				<mixed-citation publication-type="confproc">Ashcraft A. Parallel Programming and Concurrency with C# 10 and .NET 6: A modern approach to building faster, more responsive, and asynchronous .NET applications using C# / A. Ashcraft. — Packt Publishing, 2022.</mixed-citation>
			</ref>
			<ref id="B8">
				<label>8</label>
				<mixed-citation publication-type="confproc">Исходный код класса Task в .NET Runtime // GitHub: репозиторий dotnet/runtime. — URL: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs (дата обращения: 28.04.2025).</mixed-citation>
			</ref>
			<ref id="B9">
				<label>9</label>
				<mixed-citation publication-type="confproc">Исходный код класса MemoryStream в .NET Runtime // GitHub: репозиторий dotnet/runtime. — URL: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/IO/MemoryStream.cs (дата обращения: 28.04.2025).</mixed-citation>
			</ref>
			<ref id="B10">
				<label>10</label>
				<mixed-citation publication-type="confproc">Price M.J. C# 7.1 and. NET Core 2.0—Modern Cross-Platform Development: Create powerful applications with. NET Standard 2.0, ASP. NET Core 2.0, and Entity Framework Core 2.0, using Visual Studio 2017 or Visual Studio Code / M.J. Price. — Packt Publishing Ltd, 2017.</mixed-citation>
			</ref>
			<ref id="B11">
				<label>11</label>
				<mixed-citation publication-type="confproc">Balliauw M. Pro NuGet. Apress / M. Balliauw, M., X. Decoster, X., J. Handley [et al.]. — 2012.</mixed-citation>
			</ref>
			<ref id="B12">
				<label>12</label>
				<mixed-citation publication-type="confproc">Alls J. High-Performance Programming in C# and. NET: Understand the nuts and bolts of developing robust, faster, and resilient applications in C# 10.0 and. NET 6 / J. Alls. — Packt Publishing Ltd, 2022.</mixed-citation>
			</ref>
			<ref id="B13">
				<label>13</label>
				<mixed-citation publication-type="confproc">Cleary S. Concurrency in C# Cookbook: Asynchronous, Parallel, and Multithreaded Programming / S. Cleary. — O'Reilly Media, 2019.</mixed-citation>
			</ref>
			<ref id="B14">
				<label>14</label>
				<mixed-citation publication-type="confproc">Agafonov E. Multithreading with C# Cookbook / E. Agafonov. — Packt Publishing Ltd, 2016.</mixed-citation>
			</ref>
			<ref id="B15">
				<label>15</label>
				<mixed-citation publication-type="confproc">Козлов Д.А. Исследование функции Аккермана / Д.А. Козлов, Н.А. Говохин // Технические и математические науки. Студенческий научный форум. — 2018. — С. 96–110.</mixed-citation>
			</ref>
			<ref id="B16">
				<label>16</label>
				<mixed-citation publication-type="confproc">Исходный код программы AsyncAckermannBenchmark // GitHub: репозиторий CSharpCooking/AsyncAckermannBenchmark. — URL: https://github.com/CSharpCooking/AsyncAckermannBenchmark/blob/main/AsyncAckermannBenchmark/Program.cs (дата обращения: 28.04.2025).</mixed-citation>
			</ref>
			<ref id="B17">
				<label>17</label>
				<mixed-citation publication-type="confproc">Akinshin A. Pro. NET Benchmarking / A. Akinshin. — Apress, 2019.</mixed-citation>
			</ref>
		</ref-list>
	</back>
	<fundings/>
</article>