У меня есть хрупкое понимание того, как работает ключевое слово await
, и я хочу немного рассказать о нем.
Проблема, которая все еще заставляет вращаться, - это использование рекурсии. Вот пример:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestingAwaitOverflow
{
class Program
{
static void Main(string[] args)
{
var task = TestAsync(0);
System.Threading.Thread.Sleep(100000);
}
static async Task TestAsync(int count)
{
Console.WriteLine(count);
await TestAsync(count + 1);
}
}
}
Этот явно бросает a StackOverflowException
.
Я понимаю, что код действительно работает синхронно до первого асинхронного действия, после чего он возвращает объект Task
, содержащий информацию об асинхронной операции. В этом случае нет асинхронной операции, поэтому она просто рекурсирует под ложным обещанием, что в итоге она вернет Task
.
Теперь изменим его чуть-чуть:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestingAwaitOverflow
{
class Program
{
static void Main(string[] args)
{
var task = TestAsync(0);
System.Threading.Thread.Sleep(100000);
}
static async Task TestAsync(int count)
{
await Task.Run(() => Console.WriteLine(count));
await TestAsync(count + 1);
}
}
}
Это не бросает StackOverflowException
. Я могу разобраться, почему это работает, но я бы назвал это скорее чувством кишки (он, вероятно, имеет дело с тем, как код организован для использования обратных вызовов, чтобы избежать создания стека, но я не могу перевести это ощущение кишки в объяснение)
У меня есть два вопроса:
- Как вторая партия кода избегает
StackOverflowException
? - Предоставляет ли вторая партия кода другие ресурсы? (например, он выделяет абсурдно большое количество объектов Task в куче?)
Спасибо!