我正在研究将相对较小的服务从C ++重写为C#的可能性。该服务有两个主要功能:
实时任务由一个单独的库处理,该库执行自己的线程,并且根本不与服务的其余部分进行交互。服务的其余部分每隔5分钟左右就从HTTP请求中获取一些数据。
问题是,由于实时部分有很长的截止日期,我无法真正容忍库的线程上的GC暂停。在我自己的代码方面,GC应该有足够的时间在Web请求之间运行,但我不能容忍它在我尝试将数据提供给库时启动。
我found我可以创建一个关键部分,其中垃圾收集器不会使用GC.TryStartNoGCRegion()
开始,这解决了问题的一半。
但是,我仍然不知道是否有办法告诉.NET GC单独留下不运行托管代码的特定线程。这可能吗?
答案 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 :(得分:-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);
您怎么看?