递归调用方法(用于对象重用目的)

时间:2011-04-08 19:14:56

标签: c#

我有一个相当大的类,其中包含大量字段(10+),一个巨大的数组(100kb)和一些非托管资源。让我举例说明

class ResourceIntensiveClass
{
    private object unmaganedResource; //let it be the expensive resource
    private byte[] buffer = new byte[1024 * 100]; //let it be the huge managed memory
    private Action<ResourceIntensiveClass> OnComplete;


    private void DoWork(object state)
    {
        //do long running task
        OnComplete(this); //notify callee that task completed so it can reuse same object for another task
    }

    public void Start(object dataRequiredForCurrentTask)
    {
        ThreadPool.QueueUserWorkItem(DoWork); //initiate long running work
    }
}

问题是start方法在第10000次迭代后永远不会返回,导致堆栈溢出。我可以在另一个线程中执行OnComplete委托,让Start方法返回,但是如果你知道的话,它需要使用额外的cpu时间和资源。那对我来说最好的选择是什么?

6 个答案:

答案 0 :(得分:3)

是否有充分理由以递归方式进行计算?这似乎是一个简单的循环可以解决问题,从而避免了对非常深的堆栈的需求。这种设计似乎特别成问题,因为你依靠main()来设置你的递归。

答案 1 :(得分:1)

递归方法可能会非常快速地失控。您是否考虑过使用Parallel Linq? 你可以做点什么

(你的数组).AsParallel()。ForAll(item =&gt; item.CallMethod());

您还可以查看任务并行库(TPL)

通过任务,您可以定义操作并继续执行任务。

另一方面,Reactive Framework(RX)可以以异步方式处理完整事件。

答案 2 :(得分:1)

您在哪里更改taskData的值,以使其长度等于currentTaskIndex?由于您分配给数据的任务永远不会改变,因此它们将永远执行......

答案 3 :(得分:0)

我猜这个问题来自于使用预增量运算符:

 if(c.CurrentCount < 10000)
    c.Start(++c.CurrentCount);

我不确定C#中预增量的语义,也许传递给方法调用的值不是你所期望的。

但是,由于您的Start(int)方法无论如何都要将输入的值分配给this.CurrentCount,因此您应该安全地将其替换为:

 if(c.CurrentCount < 10000)
    c.Start(c.CurrentCount + 1);

分配c.CurrentCount两次没有意义。

答案 4 :(得分:0)

如果使用线程池,我假设您正在保护计数器(c.CurrentCount),否则并发增量将导致更多活动,而不仅仅是10000次执行。

答案 5 :(得分:0)

有一个称为ManualResetEvent的简洁工具可以简化你的生活。

在您的班级中放置ManualResetEvent并添加公开OnComplete活动。

当您声明课程时,您可以将OnComplete事件连接到代码中的某个位置,或者不将其连接起来并忽略它。

这有助于您的自定义类具有更正确的表单。

当你的漫长过程完成时(我猜这是在一个帖子中),只需调用ManualResetEvent的{​​{3}}方法。

至于运行你的long方法,它应该在一个使用ManualResetEvent的线程中,方式类似于下面的方法:

private void DoWork(object state)
{
    ManualResetEvent mre = new ManualResetEvent(false);
    Thread thread1 = new Thread(
      () => {
      //do long running task
      mre.Set();
    );
    thread1.IsBackground = true;
    thread1.Name = "Screen Capture";
    thread1.Start();
    mre.WaitOne();
    OnComplete(this); //notify callee that task completed so it can reuse same object for another task
}