2017-07-25 43 views
2

我有一个控制台应用程序查询数据库,然后将一些记录张贴到循环中的REST API(api不支持批量发布,所以我必须循环每个记录和单独的帖子,如果相关的话)。数据库访问速度快,没​​有问题,根据我设置的计时器,api post循环也是如此,但是应用程序本身需要很长时间才能在完成工作后退出。控制台应用程序在完成工作后需要很长时间才能退出

在我介绍Parallel.Foreach加速张贴后,开始发生这种情况。在使用非并行循环之前,发布1000条记录的平均时间为〜10分钟,但应用程序会在完成后立即返回并退出(如预期的那样)。使用并行循环后,根据我使用的Stopwatch计时器,这个计时器会减少到〜44秒的平均值,但是应用程序不会退出,直到大约2分钟过去 - 在所有工作完成后约1分15秒。

该应用程序没有做任何'额外'。它输入main,main调用一个方法从数据库中检索一些记录(1-2秒),将1000条记录转发到另一个循环遍历它们并将每个记录发送到api的方法,然后退出。除此之外,出于某种原因,它不会立即退出。

我在调用发布方法之前立即在main中放置了一个并在方法返回后立即记录时间,并且定时器与方法内部的定时器对齐,平均时间约为46秒。所以延迟发生在发布方法返回之后,但在main函数退出之前,但此时没有任何定义。调试没有显示任何异常。这是一个解除分配的问题,与并行循环产生的所有对象有关吗?

无论我是否使用附加的调试程序运行,或者在为发行版构建时直接执行二进制文件(因此不是分离延迟问题),都会发生这种情况。我已经看过这样的其他SO问题,但他们的方法并没有改变。任何输入将不胜感激。

代码张贴功能:

public ProcessingState PostClockingRecordBatchParallel(List<ClockingEvent> batch, int tokenExpiryTolerance) 
{ 
    log.Info($"Attempting to post batch of {batch.Count.ToString()} clocking records to API with an auth token expiry tolerance of {tokenExpiryTolerance} seconds"); 
    try 
    { 
     ProcessingState state = new ProcessingState() { PendingRecords = batch }; 
     List<ClockingEvent> successfulRecords = new List<ClockingEvent>(); 
     Stopwatch timer = new Stopwatch(); 

     ServicePointManager.UseNagleAlgorithm = false; //Performance optimization related to RestSharp lib 
     authToken = Authenticate(); 

     timer.Start(); 
     Parallel.ForEach(state.PendingRecords, pr => 
     { 
      successfulRecords.Add(PostClockingRecord(pr, tokenExpiryTolerance)); 
     }); 
     //Prior non-parallel version 
     //state.PendingRecords.ForEach(pr => 
     //{ 
     // successfulRecords.Add(PostClockingRecord(pr, tokenExpiryTolerance)); 
     //}); 


     state.PendingRecords  = state.PendingRecords.Except(successfulRecords).ToList(); 
     state.LastSuccessfulRecord = successfulRecords.OrderBy(r => r.EventID).Last().EventID; 

     log.Info($"PostClockingRecordBatchParallel - Time elapsed: {new TimeSpan(timer.ElapsedTicks).ToString()}"); 
     return state; 
    } 
    catch (Exception ex) 
    { 
      log.Fatal($"Failed to post records to API (exception encountered: {ex})."); 
     throw; 
    } 
} 

回答

1

是的,它会释放内存。您的线程将占用内存,您可以通过使用ParallelOptions.MaxDegreeOfParallelism Property来限制此操作,当然这会降低查询速度,并且您需要管理内存释放 - 如果您想减少退出应用程序所用的时间。

你可以dispose of your tasks,如果可扩展性是一个问题,你用尽太多的内存,或希望清理资源,你去。由于Parallel class扩展了Task类。

尽管调用垃圾回收器可能是一个更加傻瓜式的设计。

How can I free-up memory used by a Parallel.Task?

为了减少垃圾收集在运行结束时,你可以实现自己的垃圾收集,如图this answer

Action allCollect =() => 
     { 
      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
      GC.Collect(); 
     }; 

在那里你可以定期手动调用垃圾收集。

也很有帮助:
Possible memoryleak in ConcurrentBag?

This answer给出了如何使用MaxDegreeOfParallelism

ParallelOptions.MaximumDegreeOfParallelism = 1: use one full CPU (which will be a percentage of your OS CPU) 

管理,如果你想扩展您的应用程序,以避免内存泄漏和OutOfMemoryException这是很重要的例子。

+1

谢谢,正是我在找的! – Hangman

相关问题