簡體   English   中英

有沒有辦法在遞歸調用之前檢查可用的堆棧大小? (C#)

[英]Is there a way to check available stack size before recursive call? (C#)

對於C#AI程序,我使用遞歸調用來找到最佳的下一步(使用30x30陣列來存儲當前的板狀態)。 對於我所做的每一個動作,我想看看我可以從新的棋盤狀態中做出哪些可能的動作將是最好的...依此類推,直到我達到“游戲結束”的位置(此時無法進一步移動)狀態)或計時器停止進程並且不再進行進一步的遞歸調用(並且返回“最佳”已知位置)。 這只是為了解釋為什么我必須使用遞歸(它不是尾遞歸)而且我不能使用單個(全局)電路板狀態,但必須從當前狀態搜索所有電路板狀態。

(有時)我得到一個System.StackOverflowException。 有沒有辦法在下一次遞歸調用之前檢查可用的堆棧空間? 然后我可以將當​​前狀態作為“到目前為止找到的最佳位置”返回,而不是進行下一次遞歸調用。 即,當可用堆棧變得太小時,它也應該算作基本情況。

當然,另一個選擇可能是將每個遞歸調用放在try..catch塊中並使用它作為基本情況來處理System.StackOverflowException?

實際上,如果現有堆棧上的空間不足,系統將動態擴展堆棧大小。 所以,即使你可以測試堆棧的大小,也沒關系。

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686774(v=vs.85).aspx詳細信息

系統會根據需要從保留的堆棧內存中提交其他頁面,直到堆棧達到保留大小減去一頁(用作防護頁面以防止堆棧溢出)或系統內存太低以至於操作失敗”。

這就是說,在遞歸發生之前,堆棧是一個大小; 如果遞歸導致堆棧溢出,那么當發生這種情況時,堆棧是一個新的大小。

由於無法捕獲StackOverflowException而不是終端遞歸,因此可以使用尾遞歸。 以下鏈接提供了有關將終端回歸轉換為尾部回歸的一些詳細信息: http//www.thomaslevesque.com/2011/09/02/tail-recursion-in-c/

您可以使用隊列+循環( Queue<TNode> + while (queue.MoveNext()) )而不是遞歸並限制隊列的大小。

或者你可以計算對方法的開放調用並以這種方式限制遞歸。 (計算條目和退出,如果條目 - 存在> maxOpenCalls,則不輸入遞歸)。

如果您真的想沿着這條路走下去,可以使用EnsureSufficientExecutionstack方法。

正如其他人所指出的那樣,從.NET 2.0開始,您無法捕獲StackOverflowException ,但是,從MSDN文檔中您知道上一個方法具有以下行為:

確保剩余的堆棧空間足以執行平均.NET Framework功能。

根據此方法,當堆棧不夠大時,它將拋出您可以捕獲InsufficientExecutionStackException異常。

從.NET 2開始,您無法捕獲StackOverflowException ...

確定已經使用了多少堆棧的唯一方法是使用我強烈建議的不安全代碼...更好地使用顯式的基於堆的Stack<T>

實際上你可以捕獲Stackoverflow的execption,加上recursive方法必須做一些合作你做一個像這樣的方法:

void Zoo()
    {
        RuntimeHelpers.EnsureSufficientExecutionStack();
        int[] baba = new int[1024 * 5];
        Zoo();
    }

然后這樣稱呼它

 try
        {
            Zoo();
        }
        //catch (Exception ex)
        catch(InsufficientExecutionStackException ex)
        {
            ex.ProcessException().Show("Good God what are you doing");
        }

這就是進程異常方法的工作原理

public static class Helper{

[System.Runtime.InteropServices.DllImport("kernel32.dll")]
    public static extern uint GetCurrentThreadId();

public static string ProcessException(this Exception ex)
    {
        StringBuilder strBuild = new StringBuilder(5000);
        if (ex is InsufficientExecutionStackException)
        {
            strBuild.AppendLine("#%#%#%#%#% We Ran out of Stack Space on thread id : " + GetCurrentThreadId().ToString() + " @ :" + DateTime.Now.ToString() + " #%#%#%#%#%");
            strBuild.AppendLine(ex.Message);
            string[] ribals = ex.StackTrace.Split('\n');
            strBuild.AppendLine(String.Join("\n", ribals.Take(3).ToArray()));
            strBuild.AppendLine("\nLike this you can have many more lines ...\n");
            strBuild.AppendLine("Main issue  found here :\n" + ribals.Last());
            strBuild.AppendLine("#%#%#%#%#% We Ran out of Stack Space on thread id : " + GetCurrentThreadId().ToString() + " @ :" + DateTime.Now.ToString() + " #%#%#%#%#%");
            return strBuild.ToString();
        }
        Exception inner = ex;
        Enumerable.Range(0, 30).All(x =>
        {
            if (x == 0) strBuild.Append("########## Exception begin on thread id : " + GetCurrentThreadId().ToString() + " @ :" + DateTime.Now.ToString() + " ##########\n");
            strBuild.Append("---------------------[" + x.ToString() + "]---------------------\n");
            strBuild.Append("Message : " + inner.Message + "\nStack Trace : " + inner.StackTrace + "\n");
            strBuild.Append("---------------------[" + x.ToString() + "]---------------------\n");
            inner = inner.InnerException;
            if (inner == null)
            {
                strBuild.Append("########## Exception End on thread id : " + GetCurrentThreadId().ToString() + " @ :" + DateTime.Now.ToString() + " ##########\n\n");
                return false;
            }
            return true;
        });
        return strBuild.ToString();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM