GDI manipular vazamento usando TGIFImage em um segundo segmento

Eu tenho um thread de fundo que carrega imagens (a partir do disco ou um servidor), com o objetivo de, eventualmente, passá-los para o segmento principal para desenhar. Quando este segundo thread está carregando imagens GIF usando o VCLTGIFImage classe, este programaas vezes vaza várias alças toda vez que a seguinte linha é executada no encadeamento:

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

Ou seja, a imagem GIF recém aberta está sendo atribuída a um bitmap pertencente ao segmento. Nenhum deles é compartilhado com outros threads, ou seja, são totalmente localizados no thread. É dependente do tempo, por isso não ocorre sempre que a linha é executada, mas quando ocorre, acontece apenas nessa linha. Cada vazamento é um DC, uma paleta e um bitmap. (Eu usoGDIView, que fornece informações de GDI mais detalhadas que o Process Explorer.m_poBitmap32 aqui está umGraphics32 TBitmap32 objeto, mas eu tenho reproduzido isso usando classes simples VCL-only, ou seja, usandoGraphics::TBitmap::Assign.

Eventualmente eu recebo umEOutOfResources exceção, provavelmente indicando que o heap da área de trabalho está cheio:

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

Como posso resolver isso e usar com segurançaTGIFImage em um fio de fundo?

E em segundo lugar, vou encontrar esse mesmo problema com as classes PNG, JPEG ou BMP? Eu não tenho até agora, mas dado que é um problema de threading / timing que não significa que eu não vou se eles usam código semelhante aoTGIFImage.

Eu estou usando o C ++ Builder 2010 (parte do RAD Studio).

Mais detalhes

Algumas pesquisas mostraramEu não sou a única pessoa a encontrar isso. Para citar um segmento,

Ajuda (2007) diz: Em aplicativos multi-threaded que usam bloqueio para proteger uma tela, todas as chamadas que usam a tela devem ser protegidas por uma chamada para bloqueio. Qualquer thread que não bloqueie a tela antes de usá-la, apresentará possíveis bugs.

[...]

Mas essa afirmação é falsa: você DEVE travar a tela no thread secundário mesmo que outros threads não a toquem. Caso contrário, o identificador GDI da tela pode ser liberado no thread principal como não utilizado a qualquer momento (de forma assíncrona).

Outra resposta indica algo semelhante, que pode estar relacionado ao cache de objetos GDI em graphics.pas.

Isso é assustador: um objeto criado e usado inteiramente em um thread pode ter alguns de seus recursos liberados de forma assíncrona no thread principal. Infelizmente,Eu não sei como aplicar o aviso de bloqueio paraTGIFImage. TGIFImage não temCanvas, embora tenha umBitmap que tem uma tela. Bloqueio que não tem efeito. Eu suspeito que o problema está na verdadeTGIFFrame, uma aula interna. Eu também não sei se ou como eu deveria bloquear quaisquer recursos TBitmap32. Eu tentei atribuir umTMemoryBackend para o bitmap, que evita o uso de GDI, mas não teve efeito.

Reprodução

Você pode reproduzir isso com muita facilidade. Crie um novo aplicativo VCL e crie uma nova unidade que contenha um segmento. No método Execute do thread, 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>

Você pode usarGraphics::TBitmap se você não tiver o Graphics32 instalado.

No formulário principal do aplicativo, adicione um botão que cria e inicia o segmento. Adicione outro botão que execute um código semelhante ao anterior (uma única vez, sem necessidade de loop. O Mine também armazena o TBitmap32 como uma variável de membro em vez de criá-lo e invalida para que seja eventualmente pintado no formulário). e clique no botão para iniciar o tópico. Você provavelmente verá os objetos GDI vazarem, mas se não pressionar o segundo botão que executa o código semelhante uma vez no thread principal - uma vez é suficiente, parece ativar algo - e ele vazará. Você verá aumento de uso de memória e que ele vaza alças GDI na taxa de várias dezenas por segundo.

questionAnswers(1)

yourAnswerToTheQuestion