我目前正在开发一个小型模拟实用程序,使用任务并行库来提高结果的生成速度。模拟本身是一项长期的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]);
}
也许我在设置任务时采取了错误的方法?我将不胜感激任何建议或方向:)
答案 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]队列数组将作为池。