c# - ¿Puedo decirle a.NET GC que deje algunos hilos solos?
multithreading garbage-collection (2)
Como ha señalado, cuando una aplicación está configurada para ejecutarse en el modo de estación de trabajo GC, el recolector de basura no suspende los subprocesos que ejecutan el código nativo. En ese caso, cada vez que su servicio reciba una solicitud, intentaría algo como esto ...
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();
}
En un hilo diferente ...
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();
}
}
}
Esto debería ayudarlo a controlar cuándo ocurre GC para minimizar el impacto en su aplicación
En cuanto al código administrado, no he encontrado ninguna indicación de que .NET Garbage Collection pueda "dejar en paz" los subprocesos específicos que ejecutan el código administrado. Todos los modos actuales (Servidor, Estación de trabajo, Concurrente, etc.) de la recolección de basura suspenderán TODOS los subprocesos que ejecutan código administrado cuando así lo desee. Tenga en cuenta que algunos modos de GC pueden tener una pausa más corta en los hilos, lo que también podría ayudar. También he intentado usar TryStartNoGCRegion () en una aplicación c # en tiempo real e ignora mi solicitud siempre que, debido a la cantidad de memoria que utilizo, tampoco pude encontrar una forma de especificar la recopilación después de que se alcance el límite de X memoria.
Sin embargo, hay dos soluciones potenciales que puede considerar.
- Uso de notificaciones de recolección de basura para monitorear cuando el GC se aproxima a una recolección de basura completa. Potencialmente, si liberas algo de memoria manualmente antes de que llegue al punto de recolección, puedes evitar la recolección de basura.
- ¿Ejecutar los diferentes tipos de solicitudes en procesos de aplicación por separado? o pásalas a procesos de solicitud separados a medida que los recibas. De esta manera, cada aplicación no comparte el contexto y la recolección de basura se manejará por separado.
Estoy investigando la posibilidad de reescribir un servicio relativamente pequeño de C ++ a C #. El servicio tiene dos funciones principales:
- Ejecutar solicitudes HTTP de vez en cuando. Implican varias tareas de alto nivel como codificación / decodificación JSON, codificación / decodificación base64 y solicitudes HTTP en sí mismas, para las cuales C ++ no es impresionante;
- Realice una serie de tareas relacionadas con el audio en tiempo real que tienen plazos de entrega difíciles, para los que C # no es impresionante.
Las tareas en tiempo real son manejadas por una biblioteca separada que realiza sus propios procesos de subprocesos y apenas interactúa con el resto del servicio. El resto del servicio lo alimenta un poco de datos, obtenidos de las solicitudes HTTP, cada 5 minutos aproximadamente.
La cuestión es que, dado que la parte en tiempo real tiene plazos de entrega difíciles, realmente no puedo tolerar las pausas de GC en los subprocesos de la biblioteca. Por el lado de mi propio código, debería haber un montón de tiempo para que el GC se ejecute entre las solicitudes web, pero no puedo tolerar que se active mientras trato de enviar datos a la biblioteca.
Descubrí que puedo crear una sección crítica en la que el recolector de basura no comience a usar GC.TryStartNoGCRegion()
, que resuelve la mitad del problema.
Sin embargo, aún no sé si hay una manera de decirle a .NET GC que deje solo los subprocesos específicos que no ejecutan el código administrado. ¿Es eso posible?
No lo verifiqué pero, heredaría Thread, o Task, lo que sea que uses como:
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();
}
}
}
entonces puedes usarlo normalmente
var newThread = new SuppressedThread(DoWork);
newThread.Start(42);
¿Qué piensas?