Multithreading: Como usar as Classes de sincronização
Sincronizar o acesso a recursos entre threads é um problema comum ao escrever aplicativos multithread. Ter dois ou mais threads simultaneamente os mesmos dados podem levar a resultados indesejáveis e imprevisíveis de acesso. Por exemplo, um thread pode estar atualizando o conteúdo de uma estrutura enquanto outro thread está lendo o conteúdo da mesma estrutura. Ele é desconhecido que receberá o thread de leitura de dados: os dados antigos, os dados gravados recentemente ou possivelmente uma mistura de ambos. MFC fornece um número de sincronização e classes de acesso de sincronização para ajudar a resolver este problema. Este tópico explica as classes disponíveis e como usá-los para criar classes de thread-safe em um aplicativo multithreaded típico.
Um aplicativo multithreaded típico tem uma classe que representa um recurso para ser compartilhado entre segmentos. Uma classe projetada corretamente, totalmente o thread-safe não exige que você chame quaisquer funções de sincronização. Tudo é tratado internamente para a classe, permitindo que você se concentre em como usar melhor a classe, não sobre como pode obter corrompido. Uma técnica eficaz para a criação de uma classe totalmente o thread-safe é mesclar a classe de sincronização com a classe de recurso. Mesclar as classes de sincronização para a classe compartilhada é um processo simples.
Por exemplo, levar a um aplicativo que mantém uma lista de contas. Esse aplicativo permite que até três contas de ser examinado em janelas separadas, mas somente um pode ser atualizado a qualquer momento específico. Quando uma conta for atualizada, os dados atualizados são enviados pela rede para um arquivamento de dados.
O aplicativo de exemplo usa os três tipos de classes de sincronização. Porque ela permite que até três contas a ser examinado, ao mesmo tempo, ele usa CSemaphore para limitar o acesso a objetos de exibição de três. Quando uma tentativa de exibir uma quarta conta ocorre, o aplicativo ou aguarda um fecha o três primeiras windows ou ele falha. Quando uma conta for atualizada, o aplicativo usa CCriticalSection para garantir que apenas uma conta é atualizada a cada vez. Após a atualização for bem-sucedida, ele sinaliza CEvent, que lança um thread aguardando o evento deve ser sinalizado. Esse thread envia os dados de novos para o arquivamento de dados.
A criação de uma classe de Thread-Safe.
Para tornar uma classe totalmente o thread-safe, primeiro adicione a classe de sincronização apropriado para as classes compartilhadas como um membro de dados. No exemplo anterior de gerenciamento de contas, um CSemaphore o membro de dados seria adicionado à classe de modo de exibição, um CCriticalSection membro de dados seria adicionado à classe de lista vinculada e um CEvent membro de dados seria adicionado para o armazenamento de dados classe.
Em seguida, adicione as chamadas de sincronização para todas as funções de membro que modificam os dados na classe ou acessam um recurso controlado. Em cada função, você deve criar um CSingleLock ou CMultiLock de objeto e chamar do objeto Lock função. Quando o objeto de bloqueio sai do escopo e é destruído, o destruidor do objeto chama Unlock , liberando recursos. Obviamente, você pode chamar Unlock diretamente se desejar.
Projetando a sua classe thread-safe dessa maneira permite para ser usado em um aplicativo multithreaded como facilmente como uma classe de thread safe, mas com um nível maior de segurança. O objeto de sincronização e o objeto de sincronização de acesso de encapsulamento para a classe do recurso fornece todos os benefícios da programação do thread-safe totalmente sem o inconveniente de manter o código de sincronização.
O exemplo de código a seguir demonstra esse método por meio de um membro de dados, m_CritSection (do tipo CCriticalSection), declarado na classe de recurso compartilhado e um CSingleLock objeto. A sincronização do recurso compartilhado (derivado de CWinThread) é tentada pela criação de um CSingleLock objeto usando o endereço a m_CritSection objeto. É feita uma tentativa de bloquear o recurso e, quando obtido, o trabalho é feito no objeto compartilhado. Quando o trabalho for concluído, o recurso é desbloqueado com uma chamada para Unlock.
CSingleLock singleLock(&m_CritSection);
singleLock.Lock();
// resource locked
//.usage of shared resource...
singleLock.Unlock();
Observação |
---|
CCriticalSection, ao contrário de outras classes de sincronização do MFC, não tem a opção de uma solicitação de bloqueio com tempo. O período de espera para um segmento fique livre é infinito. |
As desvantagens dessa abordagem são que a classe será um pouco mais lenta do que a mesma classe sem objetos de sincronização adicionados. Além disso, se houver uma chance de que mais de um segmento pode excluir o objeto, a abordagem mesclada pode nem sempre funcionar. Nessa situação, é melhor manter os objetos de sincronização separado.
Para obter informações sobre como determinar qual classe de sincronização para usar em situações diferentes, consulte Multithreading: Quando usar as Classes de sincronização. Para obter mais informações sobre a sincronização, consulte sincronização na Windows SDK. Para obter mais informações sobre o suporte a multithreading no MFC, consulte Multithreading com C++ e MFC.