0

我目前正在开发一个小型模拟实用程序,使用任务并行库来提高生成结果的速度。模拟本身是一项长期的、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]);
       }

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

4

3 回答 3

1

您当前的实现有几个问题。一个是当与 进行交换时Leader[0],前一个领导者的引用丢失并且永远不会被释放。这可能是您的内存泄漏的根源。第二个是比较和赋值Leader[0]不是原子完成的。有可能有这样的事件序列:线程 1Leader[0]与 aresult.Value的 1 比较并为真,线程 2 与 a 的 2 比较Leader[0]并为真result.Value,线程 2 写入Leader[0],线程 1 写入Leader[0]。结果是Leader[0]最大值为 2 时的值为 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 的引用。

于 2012-06-23T22:07:36.420 回答
0

垃圾收集不会停止您的整个过程。请参阅此处了解更多信息。

如果您必须调用 GC(或您的进程终止),并且如果 GC 确实会损害您的性能(您不太可能一直执行 GC ,您总是可以将模拟分成几个进程(不要使用进程当然,每个线程,但每个 X 线程可以属于一个进程)。

不过,我不得不承认,您的内存管理可能有问题,但您需要提供更多信息。

于 2012-06-23T20:56:23.813 回答
0

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

于 2012-06-23T20:56:48.007 回答