有没有办法增加c#中的堆栈大小?
Posted
技术标签:
【中文标题】有没有办法增加c#中的堆栈大小?【英文标题】:Is there a way to increase the stack size in c#? 【发布时间】:2019-07-02 05:05:50 【问题描述】:在几年没有做过编程之后,我又回到了编程领域,并创建了一个数独游戏来让我的脚再次湿透。我编写了一个递归函数来蛮力解决方案,它会为简单的板状态执行此操作,但对于更复杂的板状态,大多数时间都会遇到堆栈溢出。我知道可以使用循环或更有效的算法来避免这种情况,但由于这是为了学习,我想知道如何为堆栈分配更多内存(承认这不是解决问题的最佳方法)。
这里的答案:How to change stack size for a .NET program? 建议使用分配更多内存的线程不起作用,因为据我所知,.NET 4.0 不会让您将线程的最大分配内存增加到默认值之外。
另一种解决方案建议使用 EDITBIN.EXE,但我是 Visual Studio 的新手,还没有找到我理解的解释。
同样,我发现您可以使用 .def 文件来指示更大的默认堆栈大小,但没有找到我理解的关于如何创建/实现的解释。
谁能提供 EDITBIN.EXE 或 .def 文件实现的新手级解释,或提供另一种增加堆栈大小的方法?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace Sudoku
//object keeps track of the value of all numbers currently on the board using an array
class BoardState
int testcount = 1;
//3d array of ints representing all values on the board, represted as region, column, row
int[,,] boardVals;
//3d array of bools representing if a number is immutable (true cannot be changed, false can be)
bool[,,] fixedVals;
//create a blank board if no initial values are provided
public BoardState()
boardVals = new int[9, 3, 3];
fixedVals = new bool[9, 3, 3];
//create a board with the listed initial values as immutable
public BoardState(int[,,] inputVals)
boardVals = inputVals;
fixedVals = new bool[9,3,3];
for (int i = 0; i < 9; ++i)
for (int j = 0; j < 3; ++j)
for (int k = 0; k < 3; ++k)
if (boardVals[i, j, k] > 0) fixedVals[i, j, k] = true;
//update the state of the board using the coordinates of a single value
//**note** method has not been implemented and tested yet
public void updateState(int region, int column, int row, int val)
if (!fixedVals[region, column, row])
boardVals[region, column, row] = val;
//update the state of the board to match the state of another board
public void updateState(int[,,] newState)
boardVals = newState;
public int[,,] getVals()
return boardVals;
public bool[,,] getFixed()
return fixedVals;
//set all non-zero values to be immutable
public void setFixed()
for (int i = 0; i < 9; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
if (boardVals[i, j, k] != 0)
fixedVals[i, j, k] = true;
else
fixedVals[i, j, k] = false;
//test method
public void testState()
for (int i = 0; i < 9; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
Console.WriteLine(boardVals[i, k, j]);
//accepts a 3d array representing the current board state.
//returns a 3d bool array denoting whether any region, row, or column is invalid (true=invalid)
//first value of array designates the region, row, or column respectively
//second value designates which of those is invalid
public bool[,] validateBoard()
bool[,] valid = new bool[3, 9];
int[,] rows = makeRows(boardVals);
int[,] cols = makeCols(boardVals);
//compare each value in each row to each other value in that row
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
//only validate an entry if it has been assigned a value
if (rows[i, j] != 0)
for (int k = 0; k < 9; k++)
//if two values are not the same entry and are equal, set that entry to invalid
if (j != k && rows[i, j] == rows[i, k])
valid[1, i] = true;
//compare each value in each column to each other value in that column
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
//only validate an entry if it has been assigned a value
if (cols[i, j] != 0)
for (int k = 0; k < 9; k++)
//if two values are not the same entry and are equal, set that entry to invalid
if (j != k && cols[i, j] == cols[i, k])
valid[2, i] = true;
//compare each value in each region to each other value in that region
for (int i = 0; i < 9; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
//only validate an entry if it has been assigned a value
if (boardVals[i, j, k] != 0)
for (int l = 0; l < 3; l++)
for (int m = 0; m < 3; m++)
//if two values are not the same entry and are equal, set that entry to invalid
if (!(l == j && m == k) && boardVals[i, j, k] == boardVals[i, l, m])
valid[0, i] = true;
return valid;
public bool isValid()
bool retFlag = true;
bool[,] valid = new bool[3, 9];
int[,] rows = makeRows(boardVals);
int[,] cols = makeCols(boardVals);
//for (int i = 0; i < 9; i++)
// for (int j = 0; j < 3; j++)
// for (int k = 0; k < 3; k++)
// Console.Write(boardVals[i, j, k]);
//compare each value in each row to each other value in that row
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
//only validate an entry if it has been assigned a value
if (rows[i, j] != 0)
for (int k = 0; k < 9; k++)
//if two values are not the same entry and are equal, set that entry to invalid
if (j != k && rows[i, j] == rows[i, k])
retFlag = false;
//compare each value in each column to each other value in that column
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
//only validate an entry if it has been assigned a value
if (cols[i, j] != 0)
for (int k = 0; k < 9; k++)
//if two values are not the same entry and are equal, set that entry to invalid
if (j != k && cols[i, j] == cols[i, k])
retFlag = false;
//compare each value in each region to each other value in that region
for (int i = 0; i < 9; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
//only validate an entry if it has been assigned a value
if (boardVals[i, j, k] != 0)
for (int l = 0; l < 3; l++)
for (int m = 0; m < 3; m++)
//if two values are not the same entry and are equal, set that entry to invalid
if (!(l == j && m == k) && boardVals[i, j, k] == boardVals[i, l, m])
retFlag = false;
return retFlag;
//returns an array of all the values in each row
public int[,] makeRows(int[,,] boardState)
int[,] rows = new int[9, 9];
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
rows[i, j] = boardState[j / 3 + ((i / 3) * 3), j % 3, i - ((i / 3) * 3)];
return rows;
//returns an array of all values in each column
public int[,] makeCols(int[,,] boardState)
int[,] cols = new int[9, 9];
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
cols[i, j] = boardState[((j / 3) * 3) + (i / 3), i - ((i / 3) * 3), j % 3];
return cols;
//update the board state to a state read in from a file
public void updateFromFile(Stream update)
int[,,] newVals = new int[9, 3, 3];
int[,,] newFixed = new int[9, 3, 3];
StreamReader file = new StreamReader(update);
for (int i = 0; i < 9; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
boardVals[i, j, k] = (int)char.GetNumericValue((char)file.Read());
for (int i = 0; i < 9; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
fixedVals[i, j, k] = (0 != ((int)char.GetNumericValue((char)file.Read())));
file.Close();
public void Solve(int entry, int val)
Console.WriteLine("This is call number " + ++testcount);
//returns if all values are filled and valid
if (entry == 81)
Console.WriteLine("Solved!");
return;
//creating reference coordinates based on entry value
int reg = entry / 9;
int col = (entry - (reg * 9)) % 3;
int row = (entry - (reg * 9)) / 3;
//if current entry being checked is a fixed value, go the next value
if (!fixedVals[reg, row, col])
Console.WriteLine("");
Console.WriteLine("Making an attempt at entry " + entry + " using value " + val);
Console.WriteLine("This entry is at region " + reg + ", col " + col + ", row " + row);
//assign entry the value to be tested
boardVals[reg, row, col] = val;
//if the value is valid, go to the next entry
if (isValid())
Console.WriteLine("Entry Valid at " + entry);
val = 1;
entry++;
Console.WriteLine("Trying the next entry at " + entry);
Solve(entry, val);
//if the value is invlid and all 9 values have not been tried,
//increment value and call again at same entry
if (!isValid() && val < 9)
Console.WriteLine("Entry Invalid at " + entry + " with value " + val);
++val;
Console.WriteLine("Trying again with value " + val);
Solve(entry, val);
//if the value in invalid and all 9 values have been tried,
//zero out the entry and go back to the previous non-fixed entry
if (!isValid() && val == 9)
do
boardVals[reg, row, col] = 0;
Console.WriteLine("Reached Value 9 and was still invalid");
--entry;
Console.WriteLine("Trying again at entry " + entry);
Console.WriteLine("The value at that entry is " + boardVals[reg, row, col]);
reg = entry / 9;
col = (entry - reg * 9) % 3;
row = (entry - reg * 9) / 3;
if (fixedVals[reg, row, col])
Console.WriteLine("But that's a fixed value, so I'll go back one more");
Console.WriteLine("");
while (boardVals[reg, row, col] == 9 || fixedVals[reg, row, col]);
val = boardVals[reg, row, col] + 1;
Solve(entry, val);
else Solve(++entry, val);
【问题讨论】:
Thread
最大堆栈大小对具有部分信任的程序集强制执行。但是,在您的计算机上构建和使用的程序集应该得到完全信任。你确定这是它不起作用的原因吗?
这是一个很好的问题,我不能给出一个可靠的答案,但我可能会承认递归不仅仅是不是最好的解决方案,它是完全错误的解决方案。使用Stack<T>
深度优先或Queue<T>
广度优先使用任意大小的托管堆栈会更好地利用您的时间。
话虽如此,关于 EDITBIN.EXE,我相信他们建议在编译的可执行文件上运行它(在构建后位于项目的 bin 文件夹中)。您可以通过将其添加为构建后事件来自动运行它(右键单击项目 > 属性 > 构建事件 > 构建后事件命令行)
是的,我已经检查过它何时溢出并且算法仍在正常运行,并且没有无限循环。我还故意创建了易于解决的状态,并且可以快速通过它们。当我不使用线程时,它会在 ~3000 次调用后溢出,当我使用线程时,它会在 ~900 次调用后溢出,无论我在创建线程时分配了多少内存。
@JacobHall - 如果您遇到堆栈溢出,那么听起来您遇到了编码问题 - 只是增加堆栈是一种创可贴,可能无法解决您的问题。无论大小,您的代码都可能只是使用所有堆栈。你能把你的代码贴出来让我们看看吗?
【参考方案1】:
或许可以在 Visual Studio 中设置 Stack Reserve Size:
项目 -> 属性 -> 配置属性 -> 链接器 -> 系统 -> 堆栈保留大小
这也可以通过命令行或在创建线程时以编程方式完成:
https://docs.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2017
【讨论】:
我使用的是 VS 2017,在项目 -> 属性中我没有“配置属性”子类别。我在属性下的选项是:imgur.com/a/QqxxzQL 我读到这可能是因为我看到的是解决方案属性而不是项目属性,但是当我在解决方案资源管理器中右键单击项目并转到这样的属性 我相信这个答案只适用于 C++ 项目,不是吗? 是的,@AlphaDelta 是正确的。这控制传递给link.exe
的命令行开关,它仅用于 C 和 C++ 构建。您甚至不会在面向 .NET 的应用程序的项目属性中看到这些选项。【参考方案2】:
严重的警告
如果您在程序中使用递归并达到 ***Exception 成为实际威胁的程度,请不要将增加堆栈大小视为有效解决方案。
如果你遇到 ***Exception 异常,你就做错了;您应该改为使用Stack<T>
进行深度优先处理,或使用Queue<T>
进行广度优先处理。 Example.
解决办法
这可以通过使用editbin.exe
来实现,它与这个包一起安装;
找到editbin.exe
的位置,我的位置是C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.14.26428\bin\Hostx64\x64\editbin.exe
,我建议使用Everything by voidtools来代替微软糟糕的搜索来找到这个。
手动设置堆栈大小
导航到您的 bin 文件夹并执行以下操作:
"<full path of editbin.exe>" /stack:<stack size in bytes, decimal> <your executable name>
例如我执行了这个:
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.14.26428\bin\Hostx64\x64\EDITBIN.EXE" /stack:2097152 ExampleProgram.exe
将堆栈保留大小设置为 2MB。
有了这个,我能够达到两倍的递归级别; (左侧为 1MB 堆栈预留,右侧为 2MB 堆栈预留)。
自动设置堆栈大小
右键单击您的项目并选择“选项”,然后单击“构建事件”并将以下内容添加到您的构建后事件中:
"<full path of editbin.exe>" /stack:<stack size in bytes, decimal> "$(TargetPath)"
比如我加了
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.14.26428\bin\Hostx64\x64\EDITBIN.EXE" /stack:2097152 "$(TargetPath)"
这将在您每次构建可执行文件时运行 editbin.exe
。
注意:当您从 Visual Studio 运行程序时,您会看到比通过资源管理器或 cmd 显式运行程序时所达到的递归级别要低得多。但是,如果从 1MB 堆栈保留移动到 2MB 堆栈保留,您仍然会看到递归级别增加了 2 倍。
【讨论】:
这正是我所需要的!我实际上不会使用递归函数来求解,它的低效率只会让我想到我想学习的这个新东西。谢谢! 更改托管程序的堆栈是老实说我从未考虑过的事情,所以这绝对是一个足够有趣的问题,即使只是为了它而仔细阅读。最后我学会了what the difference between the stack reserve and the stack commit是。 当 editbin.exe 的路径错误时,我收到了同样的错误,但我认为这可能是一般错误。尝试在 cmd 中运行命令以获取实际错误。 @AlphaDelta 我使用 C# 控制台应用程序 .NET Core 3.1 获得的最大递归级别为 24080。它比你的要少得多。任何想法?代码:`public void GetStackLimit(int index) Console.WriteLine(index); GetStackLimit(++index); `以上是关于有没有办法增加c#中的堆栈大小?的主要内容,如果未能解决你的问题,请参考以下文章