Semáforos e monitores são primitivas de sincronização usadas na programação simultânea para gerenciar o acesso a recursos compartilhados e impedir as condições de raça. No entanto, eles diferem em sua estrutura, aplicação e como são usados. Aqui está um colapso das principais diferenças:
1. Estrutura e implementação: *
semáforos: *
variáveis inteiras simples: Na sua essência, os semáforos são variáveis inteiras que são acessadas e modificadas usando operações atômicas.
*
Duas operações atômicas: Eles normalmente têm duas operações primárias:
* `wait ()` (ou `p ()` ou `adquirir ()`):diminui o valor do semáforo. Se o valor se tornar negativo, o processo/encadeamento bloqueia até que o valor do semáforo seja maior ou igual a zero.
* `Signal ()` (ou `v ()` ou `release ()`):incrementa o valor do semáforo. Se houver processos/threads bloqueados aguardando o semáforo, um deles será desbloqueado.
*
Nenhuma associação de dados implícita: Semáforos não se ligam inerentemente ao mecanismo de sincronização a dados específicos. Você pode usar um semáforo para controlar o acesso a qualquer recurso compartilhado, mas deve gerenciar a associação manualmente.
* Monitores
: *
Abordagem estruturada: Os monitores são construções de programação mais estruturadas. Eles encapsulam:
*
Dados compartilhados: Variáveis que representam o recurso compartilhado que está sendo protegido.
*
Procedimentos/Métodos: As operações que acessam e modificam os dados compartilhados. Essas são as únicas rotinas que podem acessar diretamente os dados compartilhados.
*
Variáveis de condição: (IMPORTANTE!) Variáveis especiais usadas para sinalizar e esperar dentro do monitor.
*
Exclusão mútua explícita: Os monitores fornecem exclusão mútua implícita. Apenas um thread/processo pode estar ativo * dentro * do monitor a qualquer momento. Essa exclusão mútua é automaticamente aplicada pelo próprio monitor.
2. Aplicação da exclusão mútua: *
semáforos: *
Execução manual: Os semáforos dependem de programadores para usar corretamente as operações `wait ()` e `signal ()` em torno de seções críticas. Cabe ao programador garantir que a exclusão mútua seja mantida adequadamente. Erros podem ser facilmente introduzidos. Por exemplo, esquecer um `signal ()` pode levar ao impasse. Colocar `wait ()` ou `signal ()` fora da seção crítica pretendida pode causar problemas de simultaneidade.
*
propenso a erros: Esta aplicação manual é propensa a erros. É fácil cometer erros que quebram a exclusão mútua ou causam impasse.
* Monitores
: *
Execução implícita: Os monitores aplicam exclusão mútua *implicitamente *. O idioma/sistema garante que apenas um thread possa ser ativo dentro do monitor por vez. Isso facilita o raciocínio e menos propenso a erros. O compilador ou sistema de tempo de execução lida com o bloqueio e o desbloqueio.
3. Sincronização da condição (espera e sinalização): *
semáforos: *
propósito geral, mas menos estruturado para a condição de espera: Semáforos * pode * ser usado para sincronização de condição (por exemplo, aguardando um recurso estar disponível). No entanto, é um pouco pesado. Você normalmente precisa de semáforos separados para exclusão mútua e condições de sinalização, tornando o código mais complexo e propenso a erros. Você pode usar semáforos de contagem para indicar quantos recursos estão disponíveis.
* Monitores
: *
Variáveis de condição: Os monitores usam * variáveis de condição * especificamente para sincronização de condição. Estes fornecem operações como:
* `espera (condição)`:o thread de chamada libera o bloqueio do monitor e aguarda a `condição 'especificada. A rosca é colocada em uma fila associada a essa variável de condição. O bloqueio do monitor é liberado para que outros threads possam entrar no monitor.
* `sinal (condição)`:um thread aguardando a `condição 'especificada é despertada. A rosca sinalizada reaquira a trava do monitor.
* `Broadcast (Condition)` (ou `SignalAll (Condition)`):desperta todos os threads aguardando a `condição` especificada.
*
Estrutura melhorada: As variáveis de condição estão intimamente ligadas ao monitor e seus dados compartilhados, facilitando a compreensão e a razão sobre a sincronização de condição no monitor.
4. Propriedade e responsabilidade: *
semáforos: *
sem propriedade clara: Qualquer thread pode `wait ()` ou `signal ()` em um semáforo. Não existe um conceito de "possuir" um semáforo.
* Monitores
: *
Propriedade clara: Os threads devem entrar no monitor (adquirir implicitamente o bloqueio do monitor) antes de acessar os dados compartilhados ou variáveis de condição de sinalização. Isso aplica um modelo de propriedade claro.
5. Reentração: *
semáforos: *
nenhum conceito inerente de reentrada: Os semáforos não sabem se o thread chama `wait ()` ou `signal ()` já detém o semáforo. O uso de um semáforo reenttoramente (por exemplo, o mesmo thread adquire o semáforo várias vezes sem liberá -lo) pode facilmente levar ao impasse.
* Monitores
: *
geralmente não reentrante: Os monitores geralmente são projetados para serem *não reentrantes *. Um thread que já segura a trava do monitor não pode entrar novamente no monitor. Isso simplifica o raciocínio sobre o estado do programa, mas às vezes pode exigir código de reestruturação. Algumas implementações de monitor suportam a reentrada, mas é menos comum.
em resumo: | Recurso | Semáforos | Monitores |
| -------------
| Estrutura | Variáveis inteiras simples | Construção estruturada com dados, procedimentos e variáveis de condição |
| Exclusão mútua | Manual (Responsabilidade do Programador) | Implícito (imposto pelo próprio monitor) |
| Sincronização da condição | Pode ser usado, mas menos estruturado | Explícita com variáveis de condição (espera, sinal, transmissão) |
| Erro propenso | Mais propenso a erros devido ao gerenciamento manual | Menos propensos a erros devido à aplicação implícita |
| Propriedade | Sem propriedade clara | Propriedade clara (thread deve entrar no monitor) |
| Reentração | Geralmente não é considerado | Geralmente não reentrante |
Quando usar qual: *
semáforos: Embora ainda valiosos, os semáforos são frequentemente vistos como uma primitiva de nível inferior. Eles são úteis quando é necessário um simples mecanismo de contagem ou quando os requisitos de sincronização são muito básicos. Eles também são úteis em sistemas onde os monitores não são suportados nativamente.
* Monitores
: Os monitores são preferidos ao lidar com cenários de sincronização mais complexos. Sua abordagem estruturada, exclusão mútua implícita e variáveis de condição facilitam a redação do código concorrente correto e sustentável. Os monitores são adequados para proteger estruturas de dados compartilhadas e implementar padrões de sincronização. Muitas linguagens de programação modernas fornecem suporte interno para monitores ou construções similares (por exemplo, a palavra-chave `sincronizada 'de Java e` wait () `,` notify () `,` notifyAll () `métodos ou threading.lock` e` threading).
Exemplo (conceitual - não linguagem específica): Exemplo de semáforo (conceitual): `` `
semáforo mutex =1; // semáforo binário para exclusão mútua
semáforo ResourceAvAcable =0; // Contando semáforo para recursos disponíveis
// Tópico 1 (produtor)
espere (mutex);
// Acesso e modifique o recurso compartilhado
// Adicionar recurso
sinal (mutex);
sinal (Recursoavalável); // sinaliza que um recurso agora está disponível
// Tópico 2 (consumidor)
espera (Recursovilable); // Aguarde para que um recurso fique disponível
espere (mutex);
// Acesso e modifique o recurso compartilhado
// Consuma recurso
sinal (mutex);
`` `
Monitor Exemplo (conceitual): `` `
Monitore MyMonitor {
Dados compartilhados:...;
CONDIÇÃO Recurso devastado;
método accessResource () {
// adquirir implicitamente o bloqueio do monitor
enquanto (o recurso não está disponível) {
espera (Recursovilable); // Libere o bloqueio do monitor, aguarde na condição
}
// Acesso e modifique dados compartilhados
// ...
sinal (Recursoavalável); // Tópico de espera de sinal
// Libere implicitamente o bloqueio do monitor quando o método retornar
}
método addResource () {
// adquirir implicitamente o bloqueio do monitor
// Adicione o recurso aos dados compartilhados
sinal (Recursoavalável); // Tópico de espera de sinal
// Libere implicitamente o bloqueio do monitor
}
}
`` `
No exemplo do monitor, a exclusão mútua é tratada automaticamente pelo monitor, tornando -o menos propenso a erros. As variáveis de condição fornecem uma maneira mais estruturada de lidar com a espera e a sinalização em comparação com o uso diretamente de semáforos para esse fim.