我可以告诉.NET GC单独保留一些线程吗?

时间:2016-08-02 18:33:47

标签: c# .net multithreading garbage-collection

我正在研究将相对较小的服务从C ++重写为C#的可能性。该服务有两个主要功能:

  1. 偶尔执行HTTP请求。它们涉及几个高级任务,如JSON编码/解码,base64编码/解码和HTTP请求本身,C ++并不是很棒;
  2. 执行许多具有硬截止日期的实时音频相关任务,C#并不是很棒。
  3. 实时任务由一个单独的库处理,该库执行自己的线程,并且根本不与服务的其余部分进行交互。服务的其余部分每隔5分钟左右就从HTTP请求中获取一些数据。

    问题是,由于实时部分有很长的截止日期,我无法真正容忍库的线程上的GC暂停。在我自己的代码方面,GC应该有足够的时间在Web请求之间运行,但我不能容忍它在我尝试将数据提供给库时启动。

    found我可以创建一个关键部分,其中垃圾收集器不会使用GC.TryStartNoGCRegion()开始,这解决了问题的一半。

    但是,我仍然不知道是否有办法告诉.NET GC单独留下不运行托管代码的特定线程。这可能吗?

2 个答案:

答案 0 :(得分:1)

正如您所指出的,当应用程序配置为在Workstation GC模式下运行时,垃圾收集器不会挂起执行本机代码的线程。在这种情况下,只要您的服务收到请求,我会尝试这样的事情......

    private bool _running = true;
    private int _workCounter = 0;
    private AutoResetEvent _workFlag = new AutoResetEvent(false);
    private void RunNoGCNativeCode(params object[] args)
    {
        // Increase the work counter to determine how many requests are being processed
        if (Interlocked.Increment(ref _workCounter) == 1)
        {
            // Try to start a No GC Region
            GC.TryStartNoGCRegion(1 * 1024 * 1024 * 1024, true);
        }
        // TODO: Prep data and execute your native code
        // TODO: Process response
        // TODO: Dispose of anything that is no longer in use and null objects as needed
        if (Interlocked.Decrement(ref _workCounter) == 0 && GCSettings.LatencyMode == GCLatencyMode.NoGCRegion)
        {
            GC.EndNoGCRegion();
        }
        // Notify Manual Collection thread work has been completed
        _workFlag.Set();
    }

在另一个线程上......

    private void WaitForNoWorkThenGC()
    {
        // Continue running thread while in use
        while (_running)
        {
            // Wait for some work to be complete
            _workFlag.WaitOne();
            // If there is no work being processed call GC.Collect() 
            if (_workCounter == 0)
            {
                GC.Collect();
            }
        }
    } 

这可以帮助您控制GC何时发生,以最大限度地减少对您的应用程序的影响

对于托管代码,我没有发现任何迹象表明.NET垃圾收集可以“单独”执行托管代码的特定线程。垃圾收集的所有当前模式(服务器,工作站,并发等)将在其选择时挂起执行托管代码的所有线程。请注意,某些GC模式可以在线程中具有较短的暂停,这也可能有所帮助。我也尝试在实时c#应用程序中使用TryStartNoGCRegion()并且它总是因为我使用的内存量而忽略了我的请求,我找不到在达到X内存限制之后只指定collect的方法。

<击>

但是,您可以研究两种可能的解决方案。

    <击>
  1. 使用Garbage Collection Notifications监控GC何时接近完整垃圾回收。如果你在到达收集点之前手动释放一些内存,你可以避免垃圾收集。
  2. 在不同的应用程序进程中运行不同类型的请求?或者在收到它们时将它们传递给单独的应用程序进程。这样每个应用程序都不共享上下文,垃圾收集将单独处理。

答案 1 :(得分:-2)

没检查过,但是, 无论你使用什么,我都会继承Thread或Task 像:

public class SuppressedThread : Thread
{            
    protected override void Finalize()
    {
         try
         {
          // kill what you want
         }
         finally
         {
          base.SupressFinalizeFinalize();
         }
    }
}

public class SuppressedThread : Thread, IDisposable
{    
    protected override void Dispose()
    {
         try
         {
          // kill what you want
         }
         finally
         {
          GC.SupressFinalize();
         }
    }
}

然后你可以正常使用

var newThread = new SuppressedThread(DoWork);
newThread.Start(42);

您怎么看?