Eu ouvi essas palavras relacionadas à programação simultânea, mas qual é a diferença entre elas?
concurrency
locking
mutex
semaphore
vencedor
fonte
fonte
Respostas:
Um bloqueio permite que apenas um encadeamento entre na peça que está bloqueada e o bloqueio não é compartilhado com nenhum outro processo.
Um mutex é igual a um bloqueio, mas pode ser amplo no sistema (compartilhado por vários processos).
Um semáforo faz o mesmo que um mutex, mas permite a entrada de um número x de threads; isso pode ser usado, por exemplo, para limitar o número de tarefas intensivas de CPU, io ou ram em execução ao mesmo tempo.
Para um post mais detalhado sobre as diferenças entre mutex e semáforo, leia aqui .
Você também tem bloqueios de leitura / gravação que permitem um número ilimitado de leitores ou um escritor a qualquer momento.
fonte
Existem muitos conceitos errados sobre essas palavras.
Isso é de uma postagem anterior ( https://stackoverflow.com/a/24582076/3163691 ) que se encaixa perfeitamente aqui:
1) Seção Crítica = Objeto de usuário usado para permitir a execução de apenas um encadeamento ativo de muitos outros em um processo . Os outros threads não selecionados (@ adquirindo este objeto) são colocados no modo de suspensão .
[Sem capacidade de interprocesso, objeto muito primitivo].
2) Mutex Semáforo (também conhecido como Mutex) = Objeto do kernel usado para permitir a execução de apenas um encadeamento ativo de muitos outros, entre diferentes processos . Os outros threads não selecionados (@ adquirindo este objeto) são colocados no modo de suspensão . Este objeto suporta propriedade de thread, notificação de encerramento de thread, recursão (várias chamadas de 'aquisição' do mesmo thread) e 'prevenção de inversão de prioridade'.
[Capacidade entre processos, muito segura de usar, um tipo de objeto de sincronização de 'alto nível'].
3) Counting Semaphore (aka Semaphore) = Objeto do kernel usado para permitir a execução de um grupo de threads ativos de muitos outros. Os outros threads não selecionados (@ adquirindo este objeto) são colocados no modo de suspensão .
[Contudo, a capacidade de interprocesso não é muito segura de usar porque não possui os seguintes atributos 'mutex': notificação de encerramento de encadeamento, recursão ?, 'prevenção de inversão de prioridade' ?, etc].
4) E agora, falando sobre 'spinlocks', primeiro algumas definições:
Região crítica = Uma região de memória compartilhada por 2 ou mais processos.
Lock = Uma variável cujo valor permite ou nega a entrada em uma 'região crítica'. (Pode ser implementado como um simples 'sinalizador booleano').
Espera ocupada = Teste contínuo de uma variável até que algum valor apareça.
Finalmente:
Spin-lock (aka Spinlock) = Um bloqueio que usa a espera ocupada . (A aquisição do bloqueio é feita por xchg ou operações atômicas similares ).
[Sem thread em suspensão, usada principalmente apenas no nível do kernel. Ineficiente para o código no nível do usuário].
Como último comentário, não tenho certeza, mas posso apostar muito dinheiro que os três primeiros objetos de sincronização acima (# 1, # 2 e # 3) fazem uso dessa besta simples (# 4) como parte de sua implementação.
Tenha um bom dia!.
Referências:
Conceitos em tempo real para sistemas embarcados por Qing Li com Caroline Yao (CMP Books).
- Sistemas operacionais modernos (3º) por Andrew Tanenbaum (Pearson Education International).
Aplicativos de programação para Microsoft Windows (4º) por Jeffrey Richter (Microsoft Programming Series).
Além disso, você pode dar uma olhada em: https://stackoverflow.com/a/24586803/3163691
fonte
A maioria dos problemas pode ser resolvida usando (i) apenas travas, (ii) apenas semáforos, ... ou (iii) uma combinação de ambos! Como você deve ter descoberto, eles são muito parecidos: ambos impedem condições de corrida , ambos têm
acquire()
/release()
operações, causam zero ou mais threads a serem bloqueados / suspeitos ... Realmente, a diferença crucial reside apenas em como eles bloqueiam e desbloqueiam .Para os dois bloqueios / semáforos, tentar chamar
acquire()
enquanto o primitivo está no estado 0 faz com que o encadeamento de chamada seja suspenso. Para bloqueios - as tentativas de adquirir o bloqueio estão no estado 1 com êxito. Para semáforos - as tentativas de obter o bloqueio nos estados {1, 2, 3, ...} foram bem-sucedidas.Para bloqueios no estado 0, se o mesmo encadeamento chamado anteriormente
acquire()
, agora chamar release, o release será bem-sucedido. Se um thread diferente tentou isso - cabe à implementação / biblioteca o que acontece (geralmente a tentativa é ignorada ou ocorre um erro). Para semáforos no estado 0, qualquer encadeamento pode chamar release e será bem-sucedido (independentemente de qual encadeamento usado anteriormente adquirir para colocar o semáforo no estado 0).Da discussão anterior, podemos ver que os bloqueios têm uma noção de proprietário (o único encadeamento que pode chamar a liberação é o proprietário), enquanto os semáforos não têm um proprietário (qualquer encadeamento pode chamar a liberação em um semáforo).
O que causa muita confusão é que, na prática, existem muitas variações dessa definição de alto nível.
Variações importantes a serem consideradas :
acquire()
/release()
? - [Varia massivamente ]Isso depende do seu livro / professor / idioma / biblioteca / ambiente.
Aqui está um tour rápido, observando como alguns idiomas respondem a esses detalhes.
C, C ++ ( pthreads )
pthread_mutex_t
. Por padrão, eles não podem ser compartilhados com outros processos (PTHREAD_PROCESS_PRIVATE
), no entanto, os mutex têm um atributo chamado pshared . Quando definido, o mutex é compartilhado entre processos (PTHREAD_PROCESS_SHARED
).sem_t
. Semelhante aos mutexes, os semáforos podem ser compartilhados entre threasds de muitos processos ou mantidos em sigilo nos threads de um único processo. Isso depende do argumento pshared fornecido parasem_init
.python ( threading.py )
threading.RLock
) é basicamente o mesmo que C / C ++pthread_mutex_t
s. Ambos são reentrantes . Isso significa que eles só podem ser desbloqueados pelo mesmo encadeamento que o bloqueou. É o caso desem_t
semáforos,threading.Semaphore
semáforos etheading.Lock
bloqueios não são reentrantes - pois é o caso de qualquer encadeamento que pode executar desbloquear a trava / descer o semáforo.threading.Semaphore
) é basicamente o mesmo quesem_t
. Embora comsem_t
, uma fila de IDs de encadeamentos é usada para lembrar a ordem na qual os encadeamentos foram bloqueados ao tentar bloqueá-lo enquanto está bloqueado. Quando um encadeamento desbloqueia um semáforo, o primeiro encadeamento na fila (se houver) é escolhido para ser o novo proprietário. O identificador de segmento é retirado da fila e o semáforo fica bloqueado novamente. No entanto, comthreading.Semaphore
, um conjunto é usado em vez de uma fila, portanto, a ordem na qual os segmentos foram bloqueados não é armazenada - qualquer segmento no conjunto pode ser escolhido para ser o próximo proprietário.Java ( java.util.concurrent )
java.util.concurrent.ReentrantLock
) é basicamente o mesmo que C / C ++pthread_mutex_t
e Python,threading.RLock
pois também implementa um bloqueio reentrante. Compartilhar bloqueios entre processos é mais difícil em Java por causa da JVM atuando como intermediária. Se um segmento tenta desbloquear um bloqueio que não possui, umIllegalMonitorStateException
é acionado.java.util.concurrent.Semaphore
) é basicamente o mesmo quesem_t
ethreading.Semaphore
. O construtor para semáforos Java aceita um parâmetro booleano de justiça que controla se deve ser usado um conjunto (falso) ou uma fila (verdadeiro) para armazenar os encadeamentos em espera.Em teoria, os semáforos são frequentemente discutidos, mas, na prática, os semáforos não são muito usados. Um semáforo contém apenas o estado de um número inteiro; muitas vezes é bastante inflexível e muitos são necessários ao mesmo tempo - causando dificuldade na compreensão do código. Além disso, o fato de qualquer thread poder liberar um semáforo às vezes é indesejável. Primitivas / abstrações de sincronização mais orientadas a objetos / de nível superior, como "variáveis de condição" e "monitores", são usadas.
fonte
Dê uma olhada no Tutorial Multithreading de John Kopplin.
Na seção Sincronização Entre Encadeamentos , ele explica as diferenças entre evento, bloqueio, mutex, semáforo e timer de espera
fonte
Vou tentar cobri-lo com exemplos:
Bloquear: Um exemplo em que você usaria
lock
seria um dicionário compartilhado no qual itens (que devem ter chaves exclusivas) são adicionados.O bloqueio garantiria que um thread não insira o mecanismo de código que está verificando se o item está no dicionário, enquanto outro thread (que está na seção crítica) já passou nessa verificação e está adicionando o item. Se outro encadeamento tentar inserir um código bloqueado, ele aguardará (será bloqueado) até que o objeto seja liberado.
Semáforo: Digamos que você tenha um conjunto de conexões; um único encadeamento poderá reservar um elemento no conjunto, aguardando o semáforo obter uma conexão. Em seguida, ele usa a conexão e, quando o trabalho é concluído, libera a conexão liberando o semáforo.
O exemplo de código que eu amo é um dos bouncer fornecidos por @Patric - aqui vai:
Mutex É bastante
Semaphore(1,1)
e frequentemente usado globalmente (em todo o aplicativo, caso contrário,lock
é mais apropriado). Seria usado globalMutex
ao excluir o nó de uma lista acessível globalmente (última coisa que você deseja que outro encadeamento faça algo enquanto estiver excluindo o nó). Quando você adquireMutex
se um encadeamento diferente tentar adquirir o mesmo,Mutex
ele será colocado em suspensão até o MESMO encadeamento que o adquiriuMutex
.Um bom exemplo de criação de mutex global é por @deepee
então use como:
Espero que isso poupe algum tempo.
fonte
A Wikipedia possui uma ótima seção sobre as diferenças entre semáforos e mutexes :
fonte
Meu entendimento é que um mutex é apenas para uso em um único processo, mas em seus diversos threads, enquanto um semáforo pode ser usado em vários processos e nos conjuntos correspondentes de threads.
Além disso, um mutex é binário (bloqueado ou desbloqueado), enquanto um semáforo tem uma noção de contagem ou uma fila de mais de uma solicitação de bloqueio e desbloqueio.
Alguém poderia verificar minha explicação? Estou falando no contexto do Linux, especificamente o Red Hat Enterprise Linux (RHEL) versão 6, que usa o kernel 2.6.32.
fonte
Usando a programação C em uma variante do Linux como caso base, por exemplo.
Bloqueio:
• Normalmente, um binário de construção muito simples em operação bloqueado ou desbloqueado
• Nenhum conceito de propriedade, prioridade, sequência, etc.
• Geralmente, um bloqueio rotativo em que a linha verifica continuamente a disponibilidade dos bloqueios.
• Geralmente depende de operações atômicas, por exemplo, teste e configuração, comparação e troca, busca e adição, etc.
• Geralmente requer suporte de hardware para operação atômica.
Bloqueios de arquivo:
• Geralmente usado para coordenar o acesso a um arquivo através de vários processos.
• Vários processos podem reter o bloqueio de leitura; no entanto, quando um único processo retém o bloqueio de gravação, nenhum outro processo pode adquirir um bloqueio de leitura ou gravação.
• Exemplo: rebanho, fcntl etc.
Mutex:
• As chamadas de função Mutex geralmente funcionam no espaço do kernel e resultam em chamadas do sistema.
• Utiliza o conceito de propriedade. Somente o segmento que atualmente contém o mutex pode desbloqueá-lo.
• O mutex não é recursivo (exceção: PTHREAD_MUTEX_RECURSIVE).
• Geralmente usado em Association with Condition Variables e passado como argumentos para, por exemplo, pthread_cond_signal, pthread_cond_wait etc.
• Alguns sistemas UNIX permitem que o mutex seja usado por vários processos, embora isso possa não ser imposto em todos os sistemas.
Semáforo:
• Este é um número inteiro mantido pelo kernel cujos valores não podem cair abaixo de zero.
• Pode ser usado para sincronizar processos.
• O valor do semáforo pode ser definido como um valor maior que 1; nesse caso, o valor geralmente indica o número de recursos disponíveis.
• Um semáforo cujo valor é restrito a 1 e 0 é chamado de semáforo binário.
fonte
Supporting ownership
,maximum number of processes share lock
E omaximum number of allowed processes/threads in critical section
são três os principais factores que determinam o nome / tipo do objecto simultâneo com o nome geral delock
. Como o valor desses fatores é binário (tem dois estados), podemos resumi-los em uma tabela semelhante a verdade 3 * 8.Sinta-se livre para editar ou expandir esta tabela, eu a publiquei como uma tabela ascii para ser editável :)
fonte