在循环中运行异步方法而不出现 Stackoverflow 异常

问题描述 投票:0回答:2

这段代码不断抛出

stackoverflow exception
,我有一种感觉,要么是因为
await
关键字导致堆栈填满,要么是线程可用性问题。但是,我不确定解决此问题的最佳方法是什么。

results
变量只是
StorageFiles
的集合,如果它高于1020左右,则会抛出异常;否则通常没问题。

private async void GetMusicTest()
{
    var sfolder = await StorageFolder.GetFolderFromPathAsync(dir);

    var query =  sfolder.CreateFileQueryWithOptions(queryOptions);

    var results = await query.GetFilesAsync();

    for (int i = 0; i < results.Count; i++)
    {
        MusicProperties mp = await results[i].Properties.GetMusicPropertiesAsync();
        Debug.WriteLine(mp.Title);
    }
}

此代码在控制台应用程序中运行良好,但在桌面 WinForm 应用程序中使用时会引发错误。

有趣的是,如果使用

result.Count()
,则在三次迭代后抛出错误,而
results.Count
则在迭代至少一半集合(如果不是全部)后抛出错误(似乎有所不同)。它们都返回相同的值。在不导致 stackoverflow 异常或耗尽所有可用线程的情况下循环的最佳方法是什么?

c# multithreading asynchronous windows-8 windows-runtime
2个回答
2
投票

认为这是一个应该解决的错误

如果我是对的,你可以通过偶尔在循环中执行

await Task.Yield()
来解决这个问题。


0
投票

为了清楚起见,即使在 .Net Core 9 中,这个问题仍然相关。 似乎在深度递归或其他堆栈密集型场景中运行时,强烈建议保护堆栈并偶尔使用 Task.Yield() 这是一个演示此行为的简短测试程序:

#define USE_TASK_YIELD // Enable or disable this to toggle Task.Yield behavior

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("Testing deep async recursion with stack depth...");

        try
        {
            await DeepAsyncRecursion(0);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception: {ex.Message}");
        }

        Console.WriteLine("Test completed.");
    }

    static async Task DeepAsyncRecursion(int depth)
    {
        if (depth >= 10000) // Adjust depth to push limits
        {
            Console.WriteLine($"Reached depth: {depth}");
            return;
        }

        // Print stack depth
        int stackDepth = GetStackDepth();
        Console.WriteLine($"Recursion depth: {depth}, Stack depth: {stackDepth}");

        // Safeguard with Task.Yield if defined
#if USE_TASK_YIELD
        if (GetStackDepth() > 100)
            await Task.Yield(); // Forces the continuation to run asynchronously
#endif

        // Recursive async call
        await DeepAsyncRecursion(depth + 1);

        // Simulate some work
        await Task.Delay(10);
    }

    static int GetStackDepth()
    {
        // Count the number of frames in the stack trace
        string stackTrace = Environment.StackTrace;
        int frameCount = stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None).Length;
        return frameCount;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.