Pergunta

Em Java, cada objeto possui um monitor de sincronização. Então, acho que a implementação é bastante condensada em termos de uso da memória e, esperançosamente, também rapidamente.

Ao portá -lo para C ++, qual seria a melhor implementação para ele. Eu acho que deve haver algo melhor do que "pthread_mutex_init" ou o objeto está no java realmente tão alto?

EDIT: Acabei de verificar se o pthread_mutex_t no Linux i386 é 24 bytes grandes. Isso é enorme se eu tiver que reservar esse espaço para cada objeto.

Foi útil?

Solução

O Sun Hotspot JVM implementos fino bloqueios usando Compare e trocar. Se um objeto estiver bloqueado, o segmento de espera aguarda o monitor do encadeamento que bloqueou o objeto. Isso significa que você precisa apenas de uma fechadura pesada por fio.

Outras dicas

Em certo sentido, é pior do que pthread_mutex_init, na realidade. Por causa da espera/notificar de Java, você meio que precisa de uma variável mutex e condição emparelhada para implementar um monitor.

Na prática, ao implementar uma JVM, você caça e aplica todas as otimização específica da plataforma do livro e, em seguida, invente alguns novos, para tornar os monitores o mais rápido possível. Se você não pode fazer um trabalho realmente diabólico, você definitivamente não está pronto para otimizar a coleção de lixo ;-)

Uma observação é que nem todo objeto precisa ter seu próprio monitor. Um objeto que não está sincronizado no momento não precisa de um. Portanto, a JVM pode criar um pool de monitores, e cada objeto pode ter apenas um campo de ponteiro, que é preenchido quando um thread realmente deseja sincronizar no objeto (com uma operação atômica e de comparação atômica específica da plataforma, por exemplo). Portanto, o custo da inicialização do monitor não precisa adicionar ao custo da criação de objetos. Supondo que a memória seja pré-clara, a criação de objetos pode ser: diminuir um ponteiro (mais algum tipo de verificação de limites, com uma ramificação prevista-falsa para o código que executa o GC e assim por diante); preencha o tipo; Chame o construtor mais derivado. Eu acho que você pode providenciar o construtor do objeto para não fazer nada, mas obviamente muito depende da implementação.

Na prática, o aplicativo Java médio não está sincronizando em muitos objetos a qualquer momento, portanto, os pools do monitor são potencialmente uma enorme otimização no tempo e na memória.

Não tenho certeza de como o Java faz isso, mas .NET não mantém o mutex (ou analógico - a estrutura que o mantém chamada "syncblk" lá) diretamente no objeto. Em vez disso, possui uma tabela global de Syncblks e objeto faz referência ao seu sinclk por índice nessa tabela. Além disso, os objetos não recebem um sinclk assim que são criados - em vez disso, é criado sob demanda no primeiro bloqueio.

Suponho (observe, não sei como isso realmente faz isso!) Que ele usa o atômico comparar e trocar para associar o objeto e seu Syncblk de uma maneira segura para roscas:

  1. Verifique o oculto syncblk_index Campo do nosso objeto para 0. Se não for 0, trave -o e prossiga, caso contrário ...
  2. Crie um novo Syncblk na tabela global, obtenha o índice (os bloqueios globais são adquiridos/liberados aqui, conforme necessário).
  3. Compare e troca para escrevê-lo no próprio objeto.
  4. Se o valor anterior foi 0 (suponha que 0 não seja um índice válido e é o valor inicial para o oculto syncblk_index campo de nossos objetos), nossa criação de Syncblk não foi contestada. Trava nele e prossiga.
  5. Se o valor anterior não fosse 0, alguém já havia criado um Syncblk e o associado ao objeto enquanto estávamos criando o nosso, e temos o índice desse syncblk agora. Disponha o que acabamos de criar e bloqueie o que obtivemos.

Assim, o objetivo geral é de 4 bytes (assumindo índices de 32 bits na tabela Syncblk) no melhor caso, mas maiores para objetos que realmente foram bloqueados. Se você raramente bloqueia seus objetos, esse esquema parece uma boa maneira de reduzir o uso de recursos. Mas se você precisar bloquear a maioria ou todos os seus objetos eventualmente, armazenar um mutex diretamente dentro do objeto pode ser mais rápido.

Certamente você não precisa de um monitor para todo objeto!

Ao portar de Java para C ++, isso me parece uma má idéia apenas copiar tudo cegamente. A melhor estrutura para Java não é a mesma que a melhor para C ++, principalmente porque o Java tem coleta de lixo e C ++ não.

Adicione um monitor apenas aos objetos que realmente precisam. Se apenas algumas instâncias de um tipo precisarem de sincronização, não é tão difícil criar uma classe de wrapper que contém a mutex (e possivelmente a variável de condição) necessária para a sincronização. Como outros já disseram, uma alternativa é usar um pool de objetos de sincronização com alguns meios de escolher um para cada objeto, como usar um hash do endereço do objeto para indexar a matriz.

Eu usaria a biblioteca de threads Boost ou a nova biblioteca de encadeamento padrão C ++ 0x para portabilidade, em vez de depender de especificações da plataforma a cada turno. Boost.Thread Suporta Linux, Macosx, Win32, Solaris, HP-UX e outros. Meu Implementação da biblioteca de threads C ++ 0x Atualmente, suporta apenas o Windows e o Linux, mas outras implementações estarão disponíveis no devido tempo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top