Multithreading: dicas de programação MFC
Os aplicativos de multithread exigem um cuidado mais rigoroso do que os aplicativos de thread único para garantir que as operações ocorram na ordem pretendida e que todos os dados acessados por vários threads não sejam corrompidos. Este tópico explica técnicas para evitar possíveis problemas ao programar aplicativos de multithread com a biblioteca da Classe MFC (Microsoft Foundation).
Acessando objetos de vários threads
Os objetos MFC não são thread-safe por si só. Dois threads separados não podem manipular o mesmo objeto, a menos que você use as classes de sincronização MFC e/ou os objetos de sincronização Win32 apropriados, como, por exemplo, seções críticas. Para obter mais informações sobre seções críticas e outros objetos relacionados, consulte Sincronização no SDK do Windows.
A biblioteca de classes usa seções críticas internamente para proteger estruturas de dados globais, como as usadas pela alocação de memória de depuração.
Acessando objetos MFC de threads não MFC
Se você tiver um aplicativo multithread que cria um thread de uma maneira diferente de usar um objeto CWinThread, não será possível acessar outros objetos MFC desse thread. Em outras palavras, se você quiser acessar qualquer objeto MFC de um thread secundário, deverá criar esse thread com um dos métodos descritos em Multithreading: criando threads de interface do usuário ou Multithreading: criando threads de trabalho. Esses métodos são os únicos que permitem que a biblioteca de classes inicialize as variáveis internas necessárias para lidar com aplicativos de multithread.
Mapas de identificador do Windows
Como regra geral, um thread pode acessar somente objetos MFC que ele criou. Isso ocorre porque os mapas temporários e permanentes de identificador do Windows são mantidos no armazenamento local do thread para ajudar a manter a proteção contra o acesso simultâneo de vários threads. Por exemplo, um thread de trabalho não pode executar um cálculo e, em seguida, chamar a função de membro UpdateAllViews
de um documento para ter as janelas que contêm exibições sobre os novos dados modificados. Isso não tem nenhum efeito, pois o mapa de objetos CWnd
para HWNDs é local para o thread primário. Isso significa que um thread pode ter um mapeamento de um identificador do Windows para um objeto C++, mas outro thread pode mapear esse mesmo identificador para um objeto C++ diferente. As alterações feitas em um thread não seriam refletidas na outra.
Há várias maneiras de evitar esse problema. A primeira é passar identificadores individuais (como um HWND) em vez de objetos C++ para o thread de trabalho. Em seguida, o thread de trabalho adiciona esses objetos ao seu mapa temporário chamando a função de membro FromHandle
apropriada. Você também pode adicionar o objeto ao mapa permanente do thread chamando Attach
, mas isso só deve ser feito se você tiver a garantia de que o objeto existirá mais do que o thread.
Outro método é criar novas mensagens definidas pelo usuário correspondentes às diferentes tarefas que seus threads de trabalho executarão e postarão essas mensagens na janela principal do aplicativo usando ::PostMessage
. Esse método de comunicação é semelhante a dois aplicativos diferentes conversando, exceto que ambos os threads estão sendo executados no mesmo espaço de endereço.
Para mais informações sobre mapas de identificador, confira a Nota Técnica 3. Para obter mais informações sobre o armazenamento local do thread, consulte o Armazenamento Local do Thread e o Uso do Armazenamento Local do Thread no SDK do Windows.
Comunicando entre threads
O MFC fornece várias classes que permitem que os threads sincronizem o acesso a objetos para manter a segurança do thread. O uso dessas classes é descrito em Multithreading: como usar as classes de sincronização e Multithreading: quando usar as classes de sincronização. Para mais informações sobre esses objetos, confira Sincronização no SDK do Windows.