【问题标题】:Can I tell the .NET GC to leave some threads alone?我可以告诉 .NET GC 留下一些线程吗?
【发布时间】:2016-08-02 18:33:47
【问题描述】:

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

  1. 不时执行 HTTP 请求。它们涉及一些高级任务,例如 JSON 编码/解码、base64 编码/解码和 HTTP 请求本身,而 C++ 在这些方面并不出色;
  2. 执行许多具有严格截止日期的实时音频相关任务,而 C# 在这些任务中表现不佳。

实时任务由一个单独的库处理,该库执行自己的线程处理,几乎不与服务的其余部分交互。服务的其余部分每 5 分钟左右向它提供一点点数据,这些数据是从 HTTP 请求中获得的。

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

found 表示我可以创建一个临界区,其中垃圾收集器不会使用GC.TryStartNoGCRegion() 启动,这解决了一半的问题。

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

【问题讨论】:

  • this post。它带有各种警告。
  • @ScottHannen,我也偶然发现了它(我在关于 GC.TryStartNoGCRegion 的段落中链接到它)。我认为它没有显示任何关于从垃圾收集中排除线程的信息。
  • CLR 无法挂起正忙于运行本机代码的线程。也不必,在运行此类代码时,没有对象根可以更改。因此,无需“询问”,只需确保您的时间关键代码是本机代码,并且在执行其工作时它不会返回到托管方法,并且不会出错。
  • @HansPassant,这在任何地方都有记录吗?如果是,那将是一个有效的答案。
  • @HansPassant,我发现“Fundamentals of Garbage Collection”记录了工作站式垃圾收集的行为。

标签: c# .net multithreading garbage-collection


【解决方案1】:

正如您所指出的,当应用程序配置为在工作站 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 内存限制后收集。

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

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

【讨论】:

  • 我很欣赏这项研究工作,但我想指出文档证据表明工作站垃圾收集器不会中断正在运行本机代码的线程。请参阅Fundamentals of Garbage Collection,向下滚动到“比较工作站和服务器垃圾收集”。
  • 啊,是的,我的回答是针对运行托管代码的线程。在您的 .NET 应用程序调用您的本机代码之前和之后。换句话说,您当时回答了自己的问题,“如何告诉 .NET 在 GC 期间不要暂停执行本机代码的线程?- 将您的应用程序配置为使用工作站 GC 模式,请参阅:Elementhttps://msdn.microsoft.com/en- us/library/ms229357%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396"
  • @zneak 只是想跟进一下,看看我更新的答案是否有帮助,谢谢。
【解决方案2】:

没有检查,但是, 我会继承线程或任务,无论你使用什么 喜欢:

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

你怎么看?

【讨论】:

  • 这有很多问题。 1)Thread被封号;你不能继承它。 2)即使你可以继承它,这只会阻止线程本身的GC,而不是线程上创建的对象。 3) Finalize 方法不能在 C# 中被覆盖。而是使用终结器语法,例如~Foo()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-14
  • 1970-01-01
  • 1970-01-01
  • 2022-08-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多