完成工作后,控制台应用程序需要很长时间才能退出 [英] Console app takes a long time to exit after finishing work

查看:112
本文介绍了完成工作后,控制台应用程序需要很长时间才能退出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a console app that queries a database and then posts some records to a REST API in a loop (the api does not support batch posting, so I have to loop through each record and post individually, if its relevant). The database access is fast and no issue and so is the api post loop according to the timer I've put in place, however the app itself takes a long time to exit after the work is done.

在我引入 Parallel.Foreach 以加快发布之后,这种情况开始发生。在使用非并行循环之前,发布1000条记录平均需要10分钟左右的时间,但应用程序将在完成后立即返回并退出(如预期的那样)。根据我正在使用的 Stopwatch 计时器,有了并行循环后,该时间平均减少到〜44秒,但是该应用程序直到大约2分钟过去了-完成所有工作后大约需要1分15秒。

This started happening after I introduced Parallel.Foreach to speed up the posting. Before using a non-parallel loop, posting 1000 records took on average ~10mins, but the app would return and exit immediately when it was done (as expected). With the parallel loop in place, this, according to the Stopwatch timer I'm using, is reduced to an average of ~44secs, however the app doesn't exit until around 2 minutes have passed - ~1min15sec after all work has completed.

该应用并未执行任何额外操作。它输入 main main 调用一种从数据库中检索某些记录的方法(1-2秒),转发1000将这些记录传递到另一个方法,这些方法循环遍历它们,并将每个记录发布到api,然后退出。除非出于某种原因,它在这种情况下不会立即退出。

The app isn't doing anything 'extra'. It enters main, main calls a method to retrieve some records from a database (1-2 secs), forwards 1000 of those records to another method that loops through them and posts each to the api, then exits. Except, it doesn't exit immediately in this case, for some reason.

我紧接着在 main 中放置了秒表计时器调用发布方法并在方法返回后立即记录时间,计时器与方法内部的计时器对齐,平均约46秒。因此,延迟是在返回发布方法之后但在 main 函数退出之前发生的,但是此时尚未定义任何操作。调试没有发现任何异常。

I put a stopwatch timer in main immediately before the call to the posting method and log the time immediately after the method returns, and the timer aligns with the timer inside the method, averaging ~46 seconds. So the delay is happening after the posting method has returned but before the main function exits, but there is nothing defined for it to do at this point. Debugging didn't show anything out of the ordinary. Is this a de-allocation issue related to all the objects spawned by the parallel loop that are 'hanging around'?

发生这种情况是否与并行循环产生的所有对象都有关?在为发布而构建时直接附加或执行二进制文件(因此不会出现分离延迟问题)。我已经看过其他类似的问题,但是他们的方法没有改变。

This happens regardless of whether I am running with a debugger attached or executing the binary directly when built for release (so not a detaching delay issue). I've looked at other SO questions like this but their approaches have not made a difference. Any input would be appreciated.

发布功能的代码:

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;
    }
}


推荐答案

是它将释放内存。您的线程将耗尽内存,可以使用 ParallelOptions.MaxDegreeOfParallelism属性,当然,这将减慢查询速度,并且,如果要减少退出应用程序所需的时间,则需要管理内存的重新分配。

Yes it would be freeing up the memory. Your thread will be using up memory and you can limit this by using ParallelOptions.MaxDegreeOfParallelism Property, which will then slow down the query, of course, and you need to manage the memory deallocation - if you want to reduce the time taken to exit the application.

您可以处理任务。作为并行类扩展了Task类。

You can dispose of your tasks, if scalability is an issue and you use up too much memory, or wish to clean up resources as you go. As the Parallel class extends the Task class.

尽管,对您来说,调用垃圾收集器可能更安全。

Although, calling the garbage collector may be a more foolproof design for you.

如何释放内存

要在运行结束时减少垃圾收集,您可以实现自己的垃圾收集,如< a href = https://stackoverflow.com/a/8293415/3956566>此答案

To reduce the garbage collection at the end of the run, you can implement your own garbage collection, as shown in this answer

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

在这里您可以定期手动调用垃圾收集。

Where you can periodically manually call for garbage collection.

也有帮助:

ConcurrentBag中可能存在内存泄漏吗?

此答案举例说明了如何使用 MaxDegreeOfParallelism

This answer gives examples of how to use MaxDegreeOfParallelism

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

如果要扩展应用程序,请务必进行管理,以避免内存泄漏和 OutOfMemoryException

Managing this is important if you wish to scale your application, so as to avoid memory leaks and OutOfMemoryException.

这篇关于完成工作后,控制台应用程序需要很长时间才能退出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆