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

本文关键字:对象 用于 调用 方法 递归 | 更新日期: 2023-09-27 17:49:44

我有一个相当大的类,其中包含大量字段(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时间和资源。那么对我来说什么是最好的选择呢?

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

是否有一个很好的理由来做你的计算递归?这看起来像是一个简单的循环就能达到目的,从而避免了对难以置信的深度堆栈的需要。当您依赖main()来设置递归时,这种设计似乎特别有问题。

递归方法很快就会失控。你有没有考虑过使用Parallel Linq?你可以这样写

(数组).AsParallel()。ForAll(item => item. callmethod ());

你也可以查看任务并行库(TPL)

对于任务,您可以定义一个操作和一个继续任务。

另一方面,响应式框架(RX)可以以异步方式处理这些完整的事件。

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

我猜问题是由这里使用预增量运算符引起的:

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

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

但是由于您的Start(int)方法将输入值分配给this.CurrentCount作为第一步,因此您应该安全地将其替换为:

 if(c.CurrentCount < 10000)
    c.Start(c.CurrentCount + 1);
没有必要给c.CurrentCount赋值两次。

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

有一个叫做ManualResetEvent的工具可以简化你的生活。

在你的类中放置一个ManualResetEvent,并添加一个公共OnComplete事件。

当你声明你的类时,你可以将OnComplete事件连接到代码中的某个位置,或者不连接它并忽略它。

这将有助于您的自定义类有更正确的形式。

当您的长进程完成时(我猜这是在线程中),只需调用ManualResetEvent的Set方法。

至于运行你的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
}