【问题标题】:Is there a way to find out if a method is called recursively by the same object?有没有办法找出一个方法是否被同一个对象递归调用?
【发布时间】:2023-03-30 08:53:01
【问题描述】:

我有一个带有罕见但可能的循环的对象结构。 (循环是正常的。)当使用递归方法遍历结构时,循环会导致堆栈溢出。与其向所涉及的方法添加另一个参数,不如找出“this”中的当前方法是否已经在堆栈中。这可能吗?

【问题讨论】:

  • 也许您可以使用lockMonitor.IsEntered 来检测这种情况...
  • @Johnny 在同一个线程,锁没用..
  • 我说的不是锁定而是检测递归...
  • 我在实现深拷贝算法时遇到了一个非常相似的问题。当你开始浏览这个结构时,传递一个HashSet 并将对象添加到这个HashSet 中。当您开始进一步向下结构尝试将当前对象添加到 HashSet 如果您未能添加这意味着您进入了一个循环。就这么简单,性能超级好

标签: c# recursion stack


【解决方案1】:

我可能会选择类似ThreadLocal<T> 计数器,在调用时增加它并在离开时减少它。这样,您可以分别计算它在每个线程上输入的时间。这也比扫描堆栈快得多。这也可以与布尔值一起使用,仅用于递归检查。

private ThreadLocal<int> _recursiveCounter = new ThreadLocal<int>(() => 0);

public void MyMethod(int count)
{
    _recursiveCounter.Value++;
    try
    {
        if(_recursiveCounter.Value > 2)
            Trace.WriteLine($"Recursive exceeds 2 with {_recursiveCounter.Value}");

        if(count== 0)
            return;

        MyMethod(count-1);
    }
    finally
    {
        _recursiveCounter.Value--;
    }
}

当然,您可以将其包装在一个类中并将其与 IDisposable using(...) 结合起来。

【讨论】:

  • 嗯,留下痕迹绝对是解决问题的办法。如果我理解得很好,则不可能通过堆栈,因为只有方法,但不能从框架中检索对象 ID。最后,我添加了一个堆栈变量,它记录了方法及其参数的指纹,效果很好。我会将此标记为答案,因为它激发了解决方案,谢谢!
  • @Lajos,我假设您在谈论 StackFrame 类。无论如何使用它是不可行的。因为可以通过内联等优化信息,并且开始使用它很重
  • 顺便说一句,我认为,这个问题类似于谷歌面试问题:在有限的硬件资源下,如果它包含一个循环,你能说出一个任意的单链吗?
  • 我认为这个方法的关键是你应该使用ThreadLocal&lt;T&gt;,这样你就可以确定它是同一个线程。 (所以相同的堆栈)
【解决方案2】:

我有一个非常相似的问题,以下是我解决它的方法。基本上我传递了一个HashSet&lt;object&gt; 并尝试将this 添加到这个对象中。如果添加了则this尚未处理。

// Dummy class representing an element in the structure
public class A : Strcuture { }

// Dummy class representing an element in the structure
public class B : Strcuture { }

// Dummy class representing an element in the structure
public class C : Strcuture { }

// Base Dummy class for Base Structure elements
public abstract class Strcuture {

    public List<Strcuture> Elements { get; } = new List<Strcuture>();


    public void AddElement(Strcuture element) {
        Elements.Add(element);
    }

    public void RecursiveMethod(HashSet<object> recursiveChecker) {
        if(recursiveChecker == null){
            recursiveChecker = new HashSet<object>();
        }

        var addedThis = recursiveChecker.Add(this);
        if(addedThis == false) {
            // this object has already been handled
            // throw exception?? return early etc
            throw new System.Exception("Already handled object");

        }
        foreach (var elem in Elements) {
            elem.RecursiveMethod(recursiveChecker);
        }
    }

}
class Program
{
    static void Main(string[] args)
    {
        var a = new A();
        var b = new B();
        var c = new C();
        a.AddElement(b);
        b.AddElement(c);
        c.AddElement(a);
        // now our object structure contains a loop
        // A→B→C→A→B...
        a.RecursiveMethod(new HashSet<object>());
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-11
    • 1970-01-01
    • 1970-01-01
    • 2022-01-05
    • 2011-01-16
    相关资源
    最近更新 更多