我可以告诉.NET GC不要处理一些线程吗? 我正在研究重写一个相对较小的服务从C++到C语言的可能性。该服务有两个主要功能: 偶尔执行一次HTTP请求。它们涉及到一些高级任务,如JSON编码/解码、BASE64编码/解码和HTTP请求本身,其中C++不是很棒的;李> 执行许多实时的、与音频相关的任务,这些任务都有严格的截止日期,而C#并不可怕
实时任务由一个独立的库来处理,它自己处理线程,几乎不与服务的其余部分交互。服务的其余部分每隔5分钟左右向它提供一点从HTTP请求中获得的数据 问题是,由于实时部分有严格的截止日期,我真的不能容忍库线程上的GC暂停。在我自己的代码方面,GC应该有足够的时间在Web请求之间运行,但我也不能容忍它在我尝试向库提供数据时启动 我想我可以创建一个关键部分,垃圾收集器不会在其中开始使用,这解决了一半的问题我可以告诉.NET GC不要处理一些线程吗? 我正在研究重写一个相对较小的服务从C++到C语言的可能性。该服务有两个主要功能: 偶尔执行一次HTTP请求。它们涉及到一些高级任务,如JSON编码/解码、BASE64编码/解码和HTTP请求本身,其中C++不是很棒的;李> 执行许多实时的、与音频相关的任务,这些任务都有严格的截止日期,而C#并不可怕,c#,.net,multithreading,garbage-collection,C#,.net,Multithreading,Garbage Collection,实时任务由一个独立的库来处理,它自己处理线程,几乎不与服务的其余部分交互。服务的其余部分每隔5分钟左右向它提供一点从HTTP请求中获得的数据 问题是,由于实时部分有严格的截止日期,我真的不能容忍库线程上的GC暂停。在我自己的代码方面,GC应该有足够的时间在Web请求之间运行,但我也不能容忍它在我尝试向库提供数据时启动 我想我可以创建一个关键部分,垃圾收集器不会在其中开始使用,这解决了一半的问题 但是,我仍然不知道是否有办法告诉.NETGC不要去管那些不运行托管代码的特定线程。这可能吗?正如您所指
但是,我仍然不知道是否有办法告诉.NETGC不要去管那些不运行托管代码的特定线程。这可能吗?正如您所指出的,当应用程序配置为在工作站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内存限制后收集
但是,有两种可能的解决方案可以研究
var newThread = new SuppressedThread(DoWork);
newThread.Start(42);
你觉得怎么样?看。它有各种各样的警告。@ScottHannen,我也偶然发现了它(我在关于GC.TryStartNoGCRegion的一段中链接到了它)。我不认为它显示了将线程排除在垃圾收集之外的任何内容。CLR不能挂起正在忙于运行本机代码的线程。它也不必这样做,当它运行这样的代码时,任何对象根都不能更改。因此,无需“询问”,只需确保您的时间关键型代码是本机代码,并且在执行其工作时不会返回到托管方法,并且不会出错。@HansPassant,这有文档记录吗?如果是,那将是一个有效的答案。@HansPassant,我发现“”记录了工作站式垃圾回收的行为。我感谢研究工作,但我想指出,有文档证据表明工作站垃圾回收器不会中断运行本机代码的线程。请参阅,向下滚动到“比较工作站和服务器垃圾收集”。啊,是的,我的答案是特定于运行托管代码的线程的。在.NET应用程序调用本机代码之前和之后。换句话说,您回答了自己的问题,“如何告诉.NET在GC期间不要挂起执行本机代码的线程?-将应用程序配置为使用工作站GC模式,请参阅:Elementhttps://msdn.microsoft.com/en-us/library/ms229357%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396"@zneak只是想跟进一下,看看我更新的答案是否有帮助,谢谢。这有很多问题。1)
螺纹
密封;你不能继承它。2) 即使您可以从它继承,这也只会阻止线程本身的GC,而不会阻止在线程上创建的对象。3) 无法在C#中重写Finalize
方法。而是使用终结器语法,如~Foo()
。
var newThread = new SuppressedThread(DoWork);
newThread.Start(42);