在多线程环境中回收内存 - 并行任务

时间:2012-06-23 20:45:01

标签: c# multithreading memory-leaks garbage-collection task

我目前正在开发一个小型模拟实用程序,使用任务并行库来提高结果的生成速度。模拟本身是一项长期的cpu密集型工作,它主要由运行具有不同变量的模拟的数千个较小的作业组成。

但是,每个任务使用的资源在完成所有操作之前不会释放,如果使用了足够的变量,则会导致内存泄漏和内存不足异常。在每个任务结束时强制GC释放资源,但我的理解是这需要中断所有执行的线程,因此导致接近单线程性能!

如何在长时间操作期间释放资源?

在这个上下文中的'资源'我指的是双打数组......只是很多。

public List<AnalysisTask> Questions; //Each variable combination is added as a Q

//Create a task for each simulation
Task<SimulationResults>[] tasks = new Task<SimulationResults>[Questions.Count]; 
foreach(var q in Questions)
{
    AnalysisTask temp = q
    tasks[taskCount] = Task.Factory.StartNew((t) =>
             {
                var result = EvaluateRules(temp);
                if(reults.Value > Leader[0].Value)
                    Leader[0] = result;
                else
                {
                    result.Dispose();
                    //This releases resources but interrupts threads
                    //GC.Collect(2, GCCollectionMode.Forced); 
                    return null;
                }
                return result;

             }
}

//Completion task
Task.Factory.ContinueWhenAll(tasks, (ant) =>
       {
          DoSomethingWithAnswer(Leader[0]);
       }

也许我在设置任务时采取了错误的方法?我将不胜感激任何建议或方向:)

3 个答案:

答案 0 :(得分:1)

您当前的实施有几个问题。一个是当用Leader[0]进行交换时,前一个领导者的参考文献丢失了,它永远不会被处理掉。这可能是您的内存泄漏的来源。第二个是Leader[0]的比较和分配不是原子地完成的。可以有这样的事件序列:线程1与Leader[0]比较,并且result.Value为1,线程2与Leader[0]比较,并且result.Value为真2,线程2写入Leader[0],线程1写入Leader[0]。结果是,当最大值为2时,Leader[0]的值为1.

因此,如果我们正确处理引用,您可能不需要强制进行垃圾回收。下面的代码通过在修改Leader并存储对前一个Leader[0]的引用时取出锁来修复这些问题。然后处理未使用的结果或前一个领导者。据推测,EvaluateRules需要一些时间,因此不应该有太多的锁争用。

tasks[taskCount] = Task.Factory.StartNew(() =>
     {
        var result = EvaluateRules(temp);

        var toBeDisposed = result;
        lock(Leader) // should be locking on a private object
        {
           if (result.Value > Leader[0].Value)
           {
             toBeDisposed = Leader[0];
             Leader[0] = result;
           }
        }

        toBeDisposed.Dispose();       

     });

另外,您是否需要从每项任务中返回result?您似乎只需要Leader[0]来继续执行任务。通过返回result,您将存储一个无法gc'd的引用,直到任务本身为gc'd。

答案 1 :(得分:0)

垃圾收集不会停止整个过程。有关详细信息,请参阅here

如果你必须调用GC(或你的进程死机),并且如果GC确实损害了你的性能(你不可能一直执行GC ),你总是可以打破你的模拟进入多个进程(当然,不要每个线程使用一个进程,但每个X线程都可以属于一个进程)。

但我必须承认,你的内存管理可能有问题,但你需要提供更多信息。

答案 2 :(得分:0)

如果数组是大小,或者可以定义最大大小,或者可以定义一组大小范围,您可以在启动时创建这些数组的池,或者构建一个大小数组的列表池在运行期间。然后就不需要释放数组 - 只需将它们复制以供以后重用。一个BlockingCollection [sizeRange]队列数组将作为池。