Sono, mantendo um boost :: interprocess :: scoped_lock faz com que ele nunca ser lançado
-
13-09-2019 - |
Pergunta
Estou fazendo IPC em Linux usando boost::interprocess::shared_memory_object
como por de referência (anônimo exemplo mutex).
Há um processo servidor, que cria a shared_memory_object
e escreve para ele, enquanto segurava um interprocess_mutex
envolto em um scoped_lock
; e um processo cliente que imprime tudo o que o outro tem escrito -. Neste caso, é uma int
Eu corri para um problema:. Se o servidor dorme enquanto segura a exclusão mútua, o processo cliente nunca é capaz de adquirir-lo e espera para sempre
Buggy Servidor loop:
using namespace boost::interprocess;
int n = 0;
while (1) {
std::cerr << "acquiring mutex... ";
{
// "data" is a struct on the shared mem. and contains a mutex and an int
scoped_lock<interprocess_mutex> lock(data->mutex);
data->a = n++;
std::cerr << n << std::endl;
sleep(1);
} // if this bracket is placed before "sleep", everything works
}
Servidor de saída:
acquiring mutex... 1
acquiring mutex... 2
acquiring mutex... 3
acquiring mutex... 4
Cliente loop:
while(1) {
std::cerr << "acquiring mutex... ";
{
scoped_lock<interprocess_mutex> lock(data->mutex);
std::cerr << data->a << std::endl;
}
sleep(1);
}
Cliente de saída (espera para sempre):
acquiring mutex...
A coisa é, se eu mover o suporte para a linha antes da chamada sleep
, tudo funciona. Por quê? Eu não acho que dormir com um mutex bloqueado causaria o mutex a ser eternamente trancado.
A única teoria que tenho é que quando o kernel acorda o processo do servidor, as extremidades de escopo e o mutex é liberado, mas o processo de espera não é dada a oportunidade de executar. O servidor, em seguida, re-adquire o bloqueio ... Mas isso não parece fazer muito sentido.
Obrigado!
Solução
A sua teoria está correta.
Se você olhar para a parte inferior do exemplo mutex anônimo na referência é ligada, você verá
Como podemos ver, a exclusão mútua é útil para dados proteger, mas não notificar a outro processo de um evento.
Ao soltar o mutex não notifica qualquer outra pessoa que poderia estar esperando nele, e desde que o seu processo acabou de acordar, ele quase certamente tem abundância de seu quantum de agendamento para a esquerda para fazer mais trabalho. Ele fará um loop em volta e re-adquirir o mutex antes de ele dorme novamente, o que é a primeira oportunidade que o cliente tem de adquirir o mutex em si.
Mover o exterior sleep()
servidor dos meios de escopo que vai dormir enquanto o mutex é livre, dando ao cliente a oportunidade de correr e adquirir o mutex por si.
Tente chamar sched_yield()
(Linux apenas) se você quiser desistir do processador, mas ainda sono dentro de seu escopo. sleep(0)
também pode funcionar.
Outras dicas
dormir, mantendo um mutex é errado. Mutex protege alguns dados (ou seja, data-> a) e alcance devem ser minimizados em torno de leitura / gravação de dados.