it-swarm-pt.tech

pthread_cond_wait versus semáforo

Quais são os prós/contras do uso de pthread_cond_wait ou usando um semáforo? Estou aguardando uma mudança de estado como esta:

pthread_mutex_lock(&cam->video_lock);
while(cam->status == WAIT_DISPLAY) {
    pthread_cond_wait(&cam->video_cond, &cam->video_lock);
}
pthread_mutex_unlock(&cam->video_lock);

Usando um semáforo devidamente inicializado, acho que poderia fazê-lo assim:

while(cam->status == WAIT_DISPLAY) {
    sem_wait(&some_semaphore);
}

Quais são os prós e os contras de cada método?

45
shodanex

Um semáforo é adequado para um modelo produtor-consumidor, embora tenha outros usos. A lógica do seu programa é responsável por garantir que o número certo de postagens seja feito para o número de esperas. Se você postar um semáforo e ninguém o estiver esperando ainda, então, quando o fizerem, eles continuarão imediatamente. Se o seu problema for tal que possa ser explicado em termos do valor de contagem de um semáforo, será fácil resolvê-lo com um semáforo.

Uma variável de condição é um pouco mais tolerante em alguns aspectos. Você pode, por exemplo, usar cond_broadcast para ativar todos os garçons, sem que o produtor saiba quantos existem. E se você condigna um condvar sem ninguém esperando, nada acontece. Isso é bom se você não sabe se haverá um ouvinte interessado. É também por isso que o ouvinte deve sempre verificar o estado com o mutex mantido antes de esperar - se não o fizer, poderá perder um sinal e não acordar até o próximo (o que nunca poderia ser).

Portanto, uma variável de condição é adequada para notificar as partes interessadas que o estado mudou: você adquire o mutex, altera o estado, sinaliza (ou transmite) o condvar e libera o mutex. Se isso descreve o seu problema, você está em território condvar. Se diferentes ouvintes estiverem interessados ​​em diferentes estados, basta transmitir e cada um deles acordará, descubra se encontraram o estado que desejam e, se não, espere novamente.

É realmente muito difícil tentar esse tipo de coisa com um mutex e um semáforo. O problema ocorre quando você deseja pegar o mutex, verificar algum estado e aguardar alterações no semáforo. A menos que você possa liberar atomicamente o mutex e esperar no semáforo (o que em pthreads você não pode), você acaba esperando no semáforo enquanto mantém o mutex. Isso bloqueia o mutex, o que significa que outras pessoas não podem aceitá-lo para fazer a alteração de que você gosta. Portanto, você será tentado a adicionar outro mutex de uma maneira que dependa de seus requisitos específicos. E talvez outro semáforo. O resultado geralmente é um código incorreto com condições de corrida prejudiciais.

As variáveis ​​de condição escapam desse problema, porque chamar cond_wait libera automaticamente o mutex, liberando-o para uso por outras pessoas. O mutex é recuperado antes do retorno de cond_wait.

No IIRC, é possível implementar um tipo de condvar usando apenas semáforos, mas se o mutex que você está implementando para ir com o condvar precisar de trylock, será um arranhão grave e as esperas serão encerradas. Não recomendado. Portanto, não assuma que tudo o que você pode fazer com um condvar possa ser feito com semáforos. Além disso, é claro que os mutexes podem ter bons comportamentos que os semáforos carecem, principalmente a prevenção de inversão de prioridade.

62
Steve Jessop

Os condicionais permitem fazer algumas coisas que os semáforos não fazem.

Por exemplo, suponha que você tenha algum código que exija um mutex, chamado m. No entanto, ele precisa esperar até que algum outro encadeamento termine sua tarefa, portanto, espera em um semáforo chamado s. Agora, qualquer thread que precise de m está impedido de executar, mesmo que o thread que tenha m esteja aguardando s. Esse tipo de situação pode ser resolvido usando condicionais. Quando você espera por um condicional, o mutex atualmente retido é liberado, para que outros threads possam adquirir o mutex. Então, voltando ao nosso exemplo, e suponha que condicional c foi usada em vez de s. Nosso segmento agora adquire m e aguarda condicionalmente em c. Isso libera m para que outros threads possam continuar. Quando c fica disponível, m é readquirido e nosso encadeamento original pode continuar alegre ao longo do caminho.

Variáveis ​​condicionais também permitem que você permita que todos threads aguardem uma variável condicional para prosseguir via pthread_cond_broadcast. Além disso, também permite que você execute um espera programada para que você não fique esperando para sempre.

Obviamente, às vezes você não precisa de variáveis ​​condicionais; portanto, dependendo de seus requisitos, um ou outro pode ser melhor.

19
freespace

O segundo trecho é atrevido, não faça isso.

As outras respostas têm uma boa discussão sobre os méritos relativos; Vou apenas adicionar que pthread_cond_broadcast é uma clara vantagem das variáveis ​​de condição.

Além disso, estou mais acostumado a condicionar variáveis ​​para isso, pois elas são o que você usa em Java, mesmo porque elas ajudam a evitar corridas ao verificar os sinalizadores compartilhados.

De fato, no segundo trecho, você não tem nenhum bloqueio para proteger a leitura do status da câmera, portanto ele é acessado através de uma corrida de dados. A maioria das plataformas permitirá que você se dê bem com isso neste exemplo em particular, mas que possui semântica indefinida, pelo POSIX e pelo modelo de memória dos próximos padrões C/C++.

De fato, uma condição real de corrida é possível se outro segmento alocar uma nova estrutura de came e sobrescrever a câmera; o segmento em espera pode ver a atualização no ponteiro 'cam' sem ver a inicialização do status de cam->. De fato, o segundo trecho está pedindo problemas, neste caso e em geral.

http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/

5
Blaisorblade

No seu segundo trecho, você obtém o bloqueio várias vezes, nunca o libera.

Em geral, o estado em que você está aguardando pode ser completamente expresso por um semáforo, então você pode usar exatamente isso. Uma estrutura de bloqueio é menor em tamanho e requer menos operações atômicas para verificar/configurar/liberar.

Caso contrário, se o estado for complexo e partes diferentes do código aguardarem condições diferentes da mesma variável (por exemplo, aqui você quer x <10; lá você quer y> x), use cond_wait.

0
Javier