BackgroundWorker的DoWork线程的堆栈大小是多少?有没有办法改变它?

4
我知道对于C#主程序,堆栈大小为1MB(32位和任何位)或4MB(64位),请参见为什么C#的堆栈大小恰好为1MB? BackgroundWorker DoWork线程的默认堆栈大小是多少?
除了创建另一个线程,像以下示例那样,是否有一种方法可以更改BackgroundWorker DoWork线程的堆栈大小?
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{   
    Thread thread = new Thread(delegate()
    {
        // do work with larger stack size
    }, 8192 * 1024);
    thread.Start();
    thread.Join();
}

我正在使用BackgroundWorker,因为我有一个Windows Forms应用程序,在其中我在DoWork事件中进行一些计算。我这样做是因为我想向GUI的状态行报告返回值,并且我希望用户可以取消这些计算。
我遇到了堆栈溢出错误,因为我调用了Intel MKL的LAPACKE_dtrtri,它非常递归,参见http://www.netlib.org/lapack/explore-html/df/d5c/lapacke__dtrtri_8c_source.html
以下代码显示了我如何调用Intel MKL:
public static double[,] InvTriangularMatrix(double[,] a, bool isupper)
{
    int n1 = a.GetLength(0);
    int n2 = a.GetLength(1);
    if (n1 != n2) throw new System.Exception("Matrix must be square");
    double[,] b = Copy(a);
    int matrix_layout = 101; // row-major arrays
    char uplo = isupper ? 'U' : 'L';
    char diag = 'N';
    int lda = Math.Max(1, n1);
    int info = _mkl.LAPACKE_dtrtri(matrix_layout, uplo, diag, n1, b, lda);
    if (info > 0) throw new System.Exception("The " + info + "-th diagonal element of A is zero, A is singular, and the inversion could not be completed");
    if (info < 0) throw new System.Exception("Parameter " + (-info) + " had an illegal value");
    return b;
}

并且

[DllImport(DLLName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, SetLastError = false)]
internal static extern int LAPACKE_dtrtri(
    int matrix_layout, char uplo, char diag, lapack_int n, [In, Out] double[,] a, int lda);

InvTriangularMatrix 在我的 DoWork 事件中被调用。当我没有设置堆栈大小时,LAPACKE_dtrtri 函数内会出现堆栈溢出错误。

矩阵的大小可以达到 1000 x 1000 到 100000 x 100000 的数量级。如果矩阵大于 65535 x 65535,则参见 2d-Array with more than 65535^2 elements --> Array dimensions exceeded supported range


7
BGW使用线程池线程,您无法更改其堆栈大小。对于这种解决方法,请小心,通常只需要用于修复递归代码的问题。如果在测试时出现问题,则在需要处理真实数据集的生产环境中很少会得到改善。 - Hans Passant
1
如果你发现自己需要增加堆栈大小,那么你真正应该做的是将递归方法转换为使用 Stack<T> 实现的迭代方法。 - Matthew Watson
如果您正在DoWork中创建线程,则BackgroundWorker不是您想要的。 - Andy
@PanagiotisKanavos 我使用 BackgroundWorker,因为它具有 ReportProgressCancel 功能。我启动另一个线程是因为我需要增加堆栈大小。 - Wollmich
2
它在正式上未指定,因为它取决于CLR主机,如果你的目标是Windows,则无论哪种类型都是1兆字节。这永远不足以让您满意。多逛逛,矩阵反演并不是什么难事。试试Math.NET。 - Hans Passant
显示剩余8条评论
1个回答

2
最初的回答: 在 BackgroundWorkerDoWork 事件中,堆栈大小与主线程相同。
Prof:
例如,在后构建事件中设置堆栈大小为8 MB:
"$(DevEnvDir)..\..\VC\bin\editbin.exe" /STACK:8388608 "$(TargetPath)"

然后,使用以下代码请求堆栈大小:

[DllImport("kernel32.dll")]
internal static extern void GetCurrentThreadStackLimits(out uint lowLimit, out uint highLimit);


public static uint GetStackSize()
{
    uint low, high;
    GetCurrentThreadStackLimits(out low, out high);
    return high - low;
}

在主程序和DoWork事件中使用GetStackSize,两种情况下返回8 MB或您使用EDITBIN /STACK指定的任何值。原始答案翻译成“最初的回答”。

这只在不进行调试时才有效,否则文件大小为1MB。 - Wollmich
所以在调试模式下,x86仍然是1MB,x64是4MB,对吧?至少在发布模式下,你会得到更多。 - Shpendicus
使用Visual Studio 2019,即使在调试模式下,您也可以获得指定的堆栈大小。当我写上述评论时,我还在使用Visual Studio 2013。 - Wollmich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接