У нас много вложенных асинхронных методов и мы видим поведение, которое мы действительно не понимаем. Возьмем, к примеру, это простое консольное приложение С#
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncStackSample
{
class Program
{
static void Main(string[] args)
{
try
{
var x = Test(index: 0, max: int.Parse(args[0]), throwException: bool.Parse(args[1])).GetAwaiter().GetResult();
Console.WriteLine(x);
}
catch(Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadKey();
}
static async Task<string> Test(int index, int max, bool throwException)
{
await Task.Yield();
if(index < max)
{
var nextIndex = index + 1;
try
{
Console.WriteLine($"b {nextIndex} of {max} (on threadId: {Thread.CurrentThread.ManagedThreadId})");
return await Test(nextIndex, max, throwException).ConfigureAwait(false);
}
finally
{
Console.WriteLine($"e {nextIndex} of {max} (on threadId: {Thread.CurrentThread.ManagedThreadId})");
}
}
if(throwException)
{
throw new Exception("");
}
return "hello";
}
}
}
Когда мы запускаем этот пример со следующими аргументами:
AsyncStackSample.exe 2000 false
Мы получаем исключение StackOverflowException, и это последнее сообщение, которое мы видим на консоли:
e 331 of 2000 (on threadId: 4)
Когда мы меняем аргументы на
AsyncStackSample.exe 2000 true
Мы заканчиваем этим сообщением
e 831 of 2000 (on threadId: 4)
Таким образом, StackOverflowException возникает при разматывании стека (не совсем уверен, следует ли это ему называть, но StackOverflowException возникает после рекурсивного вызова в нашем примере, в синхронном коде, исключение StackOverflow всегда возникает при вызове вложенного метода). В случае исключения исключения исключение StackOverflowException происходит еще раньше.
Мы знаем, что мы можем решить это, вызвав Task.Yield() в блоке finally, но у нас есть несколько вопросов:
- Почему стек развивается по пути разматывания (по сравнению с метод, который не вызывает переключатель потока в ожидании)?
- Почему исключение StackOverflowException происходит раньше в случае исключения, чем когда мы не генерируем исключение?