Поиск по сайту:

Как работают задачи в C#? Асинхронные/фоновые потоки


Если вы хотите выполнять веб-запросы на C# или просто хотите выполнить некоторую фоновую обработку, вам нужно будет использовать асинхронные фоновые задачи, чтобы не блокировать основной поток. Мы обсудим, что они из себя представляют и как их использовать.

Что такое асинхронное/ожидание?

Чтобы использовать Задачи, вы должны сначала понять концепцию async/await. Задачи C# не обязательно должны выполняться асинхронно, но, учитывая их единственное назначение — представление асинхронной операции, они почти всегда будут выполняться асинхронно. Вы не хотите выполнять такие операции, как получение веб-запросов и запись на жесткие диски в основном потоке, потому что это задержит остальную часть приложения (включая пользовательский интерфейс) в ожидании результата.

async/await – это специальный синтаксис, используемый для работы с асинхронными операциями. Если функция помечена как async, она обычно возвращает Task, за исключением случаев, когда обработчики событий возвращают void.

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

public async Task FetchWebResponse(string url)
{
   var response = await SendRequest(url)
}

Ожидаемое значение должно быть Task, поскольку они идут рука об руку друг с другом. Когда вы вызываете функцию SendRequest() , она возвращает Task, и программа ожидает завершения этой задачи. Вы можете думать о await как о ключевом слове, используемом для возврата или ожидания завершения задачи.

Что такое задачи?

Задачи — это оболочки, используемые для работы с асинхронными функциями. По сути, они представляют значение, которое будет возвращено в будущем. Вы можете использовать ключевое слово await, чтобы дождаться результата, или получить к нему доступ напрямую, проверив, Task.IsCompleted и затем прочитав значение Task.Result.

Вы можете создать их, написав функцию async с типом возвращаемого значения Task. Затем все, что вам нужно сделать, это вернуть значение типа T, и .NET интерпретирует это как возврат Task. Вы можете использовать await внутри этой задачи для ожидания асинхронных операций, которые, в свою очередь, сами возвращают задачу.

Вы можете запустить задачу, используя Task.Run(Action action). Это поставит задачу в очередь в пуле потоков, которая будет выполняться в фоновом режиме в другом потоке. Пул потоков принимает очередь задач и назначает их потокам ЦП для обработки. Как только они возвращаются, они помещаются в список завершенных задач, где можно получить доступ к их значениям.

Однако, несмотря на то, что это фоновый поток, очень важно использовать async/await. Если вы сделаете блокирующий вызов API в фоновом потоке и не будете ожидать его, .NET будет блокировать этот поток до его завершения, заполняя пул потоков бесполезными потоками, которые ничего не делают, кроме вредит производительности.

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

Вы также можете запускать и ждать задачи внутри других задач. Например, у вас есть функция внутри задачи DoExpensiveCalculation(), для выполнения которой требуется некоторое время. Вместо того, чтобы обрабатывать его синхронно, вы можете написать его как задачу и поставить в очередь фоновый поток в начале основной задачи. Затем, когда вам понадобится значение из этого расчета, вы можете просто ожидать задачи, и она будет выполняться до тех пор, пока задача не будет завершена и не будет возвращено возвращаемое значение.