GDI maneja la fuga usando TGIFImage en un segundo hilo

Tengo un hilo de fondo que carga imágenes (desde un disco o un servidor), con el objetivo de pasarlas al hilo principal para dibujar. Cuando este segundo hilo está cargando imágenes GIF utilizando los VCLTGIFImage clase, este programaalgunas veces pierde varios manejadores cada vez que la siguiente línea se ejecuta en el hilo:

<code>m_poBitmap32->Assign(poGIFImage);
</code>

Es decir, la imagen GIF recién abierta se asigna a un mapa de bits que es propiedad del hilo. Ninguno de estos se comparte con ningún otro subproceso, es decir, está completamente localizado en el subproceso. Es dependiente del tiempo, por lo que no ocurre cada vez que se ejecuta la línea, pero cuando ocurre ocurre solo en esa línea. Cada fuga es una DC, una paleta y un mapa de bits. (Yo sueloGDIView, que proporciona información GDI más detallada que Process Explorer.)m_poBitmap32 aquí hay unGráficos 32 TBitmap32 objeto, pero he reproducido esto utilizando clases simples de solo VCL, es decir, utilizandoGraphics::TBitmap::Assign.

Con el tiempo me sale unEOutOfResources excepción, probablemente indicando que el montón de escritorio está lleno:

<code>:7671b9bc KERNELBASE.RaiseException + 0x58
:40837f2f ; C:\Windows\SysWOW64\vclimg140.bpl
:40837f68 ; C:\Windows\SysWOW64\vclimg140.bpl
:4084459f ; C:\Windows\SysWOW64\vclimg140.bpl
:4084441a vclimg140.@Gifimg@TGIFFrame@Draw$qqrp16Graphics@TCanvasrx11Types@TRectoo + 0x4a
:408495e2 ; C:\Windows\SysWOW64\vclimg140.bpl
:50065465 rtl140.@Classes@TPersistent@Assign$qqrp19Classes@TPersistent + 0x9
:00401C0E TLoadingThread::Execute(this=:00A44970)
</code>

¿Cómo resuelvo esto y uso de forma segura?TGIFImage en un hilo de fondo?

Y en segundo lugar, ¿tendré este mismo problema con las clases PNG, JPEG o BMP? No lo he hecho hasta ahora, pero dado que es un problema de subprocesos / sincronización, no significa que no lo haré si usan un código similar alTGIFImage.

Estoy usando C ++ Builder 2010 (parte de RAD Studio).

Más detalles

Algunas investigaciones mostraronNo soy la única persona que encuentra esto. Para citar de un hilo,

La Ayuda (2007) dice: En las aplicaciones de subprocesos múltiples que usan Bloquear para proteger un lienzo, todas las llamadas que usan el lienzo deben estar protegidas por una llamada a Bloquear. Cualquier hilo que no bloquee el lienzo antes de usarlo introducirá posibles errores.

[...]

Pero esta afirmación es absolutamente falsa: DEBE bloquear el lienzo en un hilo secundario, incluso si otros hilos no lo tocan. De lo contrario, el identificador GDI del lienzo se puede liberar en el hilo principal como no utilizado en cualquier momento (de forma asíncrona).

Otra respuesta indica algo similar, que puede tener que ver con el caché de objetos GDI en graphics.pas.

Eso da miedo: un objeto creado y utilizado completamente en un hilo puede tener algunos de sus recursos liberados de forma asíncrona en el hilo principal. Desafortunadamente,No sé cómo aplicar el consejo de bloqueo aTGIFImage. TGIFImage no tieneCanvas, aunque tiene unBitmap que tiene un lienzo. Bloqueo que no tiene efecto. Sospecho que el problema está realmente enTGIFFrame, una clase interna. Tampoco sé si o cómo debo bloquear los recursos de TBitmap32. Intenté asignar unTMemoryBackend al mapa de bits, que evita el uso de GDI, pero no tuvo ningún efecto.

Reproducción

Puedes reproducir esto muy fácilmente. Cree una nueva aplicación VCL y cree una nueva unidad que contenga un hilo. En el método de ejecución del hilo, coloque este código:

<code>while (!Terminated) {
    TGraphic* poGraphic = new TGIFImage();
    TBitmap32* poBMP32 = new TBitmap32();
    __try {
        poGraphic->LoadFromFile(L"test.gif");
        poBMP32->Assign(poGraphic);
    } __finally {
        delete poBMP32;
        delete poGraphic;
    }
}
</code>

Puedes usarGraphics::TBitmap Si no tienes instalado Graphics32.

En el formulario principal de la aplicación, agregue un botón que cree e inicie el hilo. Agregue otro botón que ejecute un código similar al anterior (solo una vez, no es necesario realizar un bucle. Mine también almacena el TBitmap32 como una variable miembro en lugar de crearlo allí, e invalida para que finalmente lo dibuje en el formulario). Ejecute el programa y haga clic en el botón para iniciar el hilo. Probablemente ya verás una fuga de objetos GDI, pero si no, presiona el segundo botón que ejecuta el código similar una vez en el hilo principal (una vez es suficiente, parece que se activa algo) y se producirá una fuga. Verá un aumento en el uso de la memoria, y que pierde los controles de GDI a una velocidad de varias docenas por segundo.

Respuestas a la pregunta(1)

Su respuesta a la pregunta