【问题标题】:Recursive method call results in StackOverflow exception递归方法调用导致 StackOverflow 异常
【发布时间】:2017-09-13 11:08:37
【问题描述】:

写完这个问题并创建了一个 MCVE,这听起来有点像家庭作业,但实际上不是...... 不幸的是,我太老了,不能布置作业。

我正在尝试编写一个小应用程序来填充我正在使用的“金字塔”结构的数据库。 原会员1人,推荐10人。 这些推荐中的每一个都可以有 10 个推荐。每个人都有 10 个推荐人。等等……

我正在尝试用最大数量的成员(已提供)填充数据库

如果我将最大成员数设置为 100,000 - 这可行。 但是,200,000 会引发 StackOverflow 异常。

我很确定这取决于我没有足够早地终止“扇出”。但我终生无法弄清楚在哪里。

注意,为了简单起见,我下面的 MCVE 使用 Dapper,因此使用简单的 INSERT INTO 语句

public class MemberPopulator
{
    private readonly SqlConnection connection;

    private const string MemberSql = @"
            INSERT INTO Members (FirstName, LastName, ReferralId, Active, Created) 
            VALUES (@FirstName, @LastName, @ReferralId, @Active, @Created);
            SELECT CAST(SCOPE_IDENTITY() as int)";

    private int TotalMemberCount;
    private const int MaxMemberCount = 200000;

    public MemberPopulator()
    {
        connection = new SqlConnection("Data Source=localhost;Initial Catalog=MyTestDb;Integrated Security=True");
    }

    public void CreateMembers()
    {
        //clear members
        connection.Execute("TRUNCATE TABLE Members");

        //create the 'original' member (top of pyramid)
        var originalMemberId = connection.Query<int>(MemberSql, new Member
        {
            FirstName = "FirstName Goes Here",
            ReferralId = 0
        }).Single();

        //now we have 1 total members
        TotalMemberCount = 1;

        //recursively create members, starting with original member,
        RecursiveCreate(new[] { originalMemberId });
    }

    private void RecursiveCreate(IEnumerable<int> referralMemberIds)
    {
        //don't recurse if we've already got enough members
        if (TotalMemberCount >= MaxMemberCount)
            return;

        foreach (var referralId in referralMemberIds)
        {
            //Create 10 members
            var refs = CreateReferredMembers(referralId, 10);

            RecursiveCreate(refs);
        }
    }

    private IEnumerable<int> CreateReferredMembers(int referralId, int numberOfReferrals)
    {
        var referredMemberIds = new List<int>();

        for (var i = 0; i < numberOfReferrals; i++)
        {
            if (TotalMemberCount >= MaxMemberCount)
                break;

            var member = new Member
            {
                FirstName = "FirstName Goes Here",
                ReferralId = referralId
            };

            var memberId = connection.Query<int>(MemberSql, member).Single();
            referredMemberIds.Add(memberId);
            TotalMemberCount++;
        }

        return referredMemberIds;
    }
}

【问题讨论】:

  • 只有当你知道递归级别最小时,递归才是可行的。 20万已经很多了!请记住,递归很好,但总是可以用其他循环代替。从来没有必要。
  • ^ 他说的。但是如果你坚持使用递归,你可以试试setting the stack size到更大的值。

标签: c# recursion


【解决方案1】:

C# 中的堆栈设置为 1MB 用于 32 位应用程序或 4MB 用于 64 位应用程序by default。这适用于大多数应用程序。如果您需要更多,请按照网上的指导(例如this one)。

如果您不知道确切的递归级别,我建议您使用 StackQueue 数据类型来模拟递归。

public class MemberPopulator
{
    private readonly SqlConnection connection;

    private const string MemberSql = @"
            INSERT INTO Members (FirstName, LastName, ReferralId, Active, Created) 
            VALUES (@FirstName, @LastName, @ReferralId, @Active, @Created);
            SELECT CAST(SCOPE_IDENTITY() as int)";

    private int TotalMemberCount;
    private const int MaxMemberCount = 200000;

    public MemberPopulator()
    {
        connection = new SqlConnection("Data Source=localhost;Initial Catalog=MyTestDb;Integrated Security=True");
    }

    public void CreateMembers()
    {
        //clear members
        connection.Execute("TRUNCATE TABLE Members");

        //create the 'original' member (top of pyramid)
        var originalMemberId = connection.Query<int>(MemberSql, new Member
        {
            FirstName = "FirstName Goes Here",
            ReferralId = 0
        }).Single();

        //now we have 1 total members
        TotalMemberCount = 1;

        //recursively create members, starting with original member,
        NonRecursiveCreate(originalMemberId);
    }

    private void NonRecursiveCreate(int root)
    {
        Queue<int> members = new Queue<int>();

        members.Enqueue(root);

        while (members.Any() && TotalMemberCount < MaxMemberCount)
        {

            var referralId = members.Dequeue();
            //Create 10 members
            var refs = CreateReferredMembers(referralId, 10);
            foreach (int i in refs)
            {
                members.Enqueue(i);
            }
        }
    }

    private IEnumerable<int> CreateReferredMembers(int referralId, int numberOfReferrals)
    {
        var referredMemberIds = new List<int>();

        for (var i = 0; i < numberOfReferrals; i++)
        {
            if (TotalMemberCount >= MaxMemberCount)
                break;

            var member = new Member
            {
                FirstName = "FirstName Goes Here",
                ReferralId = referralId
            };

            var memberId = connection.Query<int>(MemberSql, member).Single();
            referredMemberIds.Add(memberId);
            TotalMemberCount++;
        }

        return referredMemberIds;
    }
}

【讨论】:

  • 感谢您的帮助!我投了赞成票,但显然它不会显示,因为我的“代表”太低了!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-21
  • 2018-02-05
  • 2018-01-03
  • 1970-01-01
  • 2013-11-15
  • 1970-01-01
  • 2016-10-14
相关资源
最近更新 更多