Pergunta

Alguém sabe de uma implementação shared_ptr totalmente thread-safe? Por exemplo. boost implementação de shared_ptr é thread-safe para os alvos (refcounting) e também seguro por exemplo shared_ptr simultânea lê, mas não escreve, ou para leitura / gravação.

(ver docs impulso , exemplos 3, 4 e 5).

Existe uma implementação shared_ptr que está totalmente thread-safe para instâncias shared_ptr?

Estranho que a impulsionar as docs dizer que:

objetos shared_ptr oferecer o mesmo nível de segurança do thread como built-in tipos.

Mas se você comparar um ponteiro comum (tipo built-in) para smart_ptr, escrita depois simultânea de um ponteiro comum é thread-safe, mas gravação simultânea a um smart_ptr não é.

EDIT:. Quero dizer uma implementação livre-lock na arquitetura x86

EDIT2: Um exemplo de caso de uso para um ponteiro tão inteligente seria onde há um número de threads de trabalho que atualizar uma shared_ptr global com seu item de trabalho atual e um fio monitor que toma amostras aleatórias dos itens de trabalho. A-ptr compartilhada possuiria o item de trabalho até que outro ponteiro item de trabalho é atribuído a ele (destruindo assim o item de trabalho anterior). O monitor obteria a propriedade do item de trabalho (impedindo assim o item de trabalho para ser destruído), atribuindo-o para o seu próprio-ptr compartilhada. Isso pode ser feito com XCHG e exclusão manual, mas seria bom se a-ptr compartilhada poderia fazê-lo.

Outro exemplo é onde a shared-ptr mundial tem um "processador", e é atribuído por alguns fios, e utilizado por algum outro segmento. Quando o segmento "usuário" vê que o processador caco-ptr é NULL, ele usa alguma lógica alternativa para fazer o processamento. Se não for NULL, ele impede que o processador de ser destruída por atribuí-la a seu próprio-ptr compartilhado.

Foi útil?

Solução

Adicionando as barreiras necessárias para tal um totalmente implementação thread-safe shared_ptr faria desempenho provável impacto. Considere o seguinte corrida (nota: abunda pseudocódigo):

Thread 1: global_ptr = A;

Thread 2: global_ptr = B;

Tópico 3: local_ptr = global_ptr;

Se decompô-lo em suas operações constituintes:

Thread 1:

A.refcnt++;
tmp_ptr = exchange(global_ptr, A);
if (!--tmp_ptr.refcnt) delete tmp_ptr;

Thread 2:

B.refcnt++;
tmp_ptr = exchange(global_ptr, B);
if (!--tmp_ptr.refcnt) delete tmp_ptr;

Tópico 3:

local_ptr = global_ptr;
local_ptr.refcnt++;

Claramente, se o fio 3 lê o ponteiro após troca de A, então B vai e exclui-lo antes que a contagem de referência pode ser incrementado, coisas ruins vão acontecer.

Para lidar com isso, precisamos de um valor fictício para ser usado enquanto fio 3 está fazendo a atualização refcnt: (Nota: compare_exchange (variável, espera, novo) atomicamente substitui o valor na variável com nova se está actualmente igual a novo, em seguida, retorna verdadeiro se o fez com sucesso)

Thread 1:

A.refcnt++;
tmp_ptr = global_ptr;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
    tmp_ptr = global_ptr;
if (!--tmp_ptr.refcnt) delete tmp_ptr;

Thread 2:

B.refcnt++;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
    tmp_ptr = global_ptr;
if (!--tmp_ptr.refcnt) delete tmp_ptr;

Tópico 3:

tmp_ptr = global_ptr;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, BAD_PTR))
    tmp_ptr = global_ptr;
local_ptr = tmp_ptr;
local_ptr.refcnt++;
global_ptr = tmp_ptr;

Você já teve que adicionar um loop, com atomics nele no meio do seu / ler / operação. Esta não é uma coisa boa - pode ser extremamente caro em algumas CPUs. Além do mais, você está ocupado-à espera também. Você pode começar a ficar esperto com futexes e outros enfeites -., Mas por esse ponto você já reinventou o fechamento

Este custo, que tem a cargo a cada operação, e é muito semelhante na natureza do que um bloqueio iria dar-lhe de qualquer maneira, é porque você geralmente não ver essas implementações shared_ptr thread-safe. Se você precisar de uma coisa dessas, eu recomendaria o envolvimento de um mutex e shared_ptr em uma classe de conveniência para bloqueio automatizar.

Outras dicas

gravação simultânea para um ponteiro incorporado for certamente não thread-safe. Considere as implicações de escrever para o mesmo valor em relação a barreiras de memória, se você realmente quer deixar você louca (por exemplo, você poderia ter dois tópicos que pensam o mesmo ponteiro tinham valores diferentes).

RE: Comentário - a razão built-ins não são dupla exclusão é porque não está excluindo a todos (e a implementação do uso de boost :: shared_ptr eu não faria dupla eliminação, uma vez que utiliza um incremento atômica especial e decréscimo, por isso só seria única de exclusão, mas, em seguida, o resultado seria poderia ter o ponteiro de um ea contagem ref do outro. Ou praticamente qualquer combinação dos dois. seria ruim.). A declaração nos impulsionar docs está correto, pois é, você tem as mesmas garantias que você faz com um built-in.

RE: EDIT2 - A primeira situação que você está descrevendo são muito diferentes entre o uso de built-ins e shared_ptrs. Em um (XCHG e manual de exclusão) não há nenhum contagem de referência; você está supondo que você é o único proprietário quando você faz isso. Se usando ponteiros compartilhados, você está dizendo outros tópicos pode ter a propriedade, o que torna as coisas muito mais complexo. Eu acredito que é possível com uma-and-swap comparar, mas isso seria muito não-portáteis.

C ++ 0x está saindo com uma biblioteca Atomics, que deve torná-lo muito mais fácil escrever código multi-thread genérico. Você provavelmente vai ter que esperar até que sai para ver bons implementações de referência multi-plataforma de ponteiros inteligentes thread-safe.

Eu não sei de uma tal implementação ponteiro inteligente, embora eu tenho que perguntar: como poderia este comportamento pode ser útil? Os únicos cenários que eu posso pensar de onde você iria encontrar atualizações ponteiro simultâneas são condições de corrida (ou seja bugs).

Esta não é uma crítica - pode muito bem haver um caso de uso legítimo, eu não posso pensar nisso. Por favor me avise!

Re: EDIT2 Obrigado por fornecer um par de cenários. Soa como escreve ponteiro atômicas seria útil nessas situações. (Uma pequena coisa: para o segundo exemplo, quando você escreveu "Se não for NULL, ele impede que o processador de ser destruída por atribuí-la a seu próprio-ptr compartilhada", espero que significava que você atribui o ponteiro compartilhada global para o ponteiro local compartilhado primeira então verificar se o ponteiro compartilhado local é NULL - a maneira que você descreveu é propenso a uma condição de corrida onde o ponteiro compartilhado global se torna NULL depois de teste para ele e antes de atribuí-la para o local.)

Você pode usar esta implementação Atomic Referência ponteiros Contando para, pelo menos, implementar o mecanismo de contagem de referência.

Seu compilador pode já fornecem o fio ponteiros inteligentes seguros nas normas mais recentes de C ++. Acredito TBB está pensando em adicionar um ponteiro inteligente, mas eu não acho que foi incluído ainda. Você pode ser capaz de usar um dos recipientes thread-safe da TBB, no entanto.

Você pode facilmente fazer isso, incluindo um objeto mutex com cada ponteiro compartilhada, e empacotamento aumentar / diminuir comandos com o bloqueio.

Eu não acho que esta tão fácil, não é suficiente para envolver suas classes sh_ptr com um CS. É verdade que se você manter um único CS para todos os ponteiros compartilhados pode garantir para evitar mútua acesso e exclusão de objetos sh_ptr entre diferentes threads. Mas isso seria terrível, um objeto CS para cada ponteiro compartilhada seria um verdadeiro gargalo. Seria adequado se cada Wrappable novas -s PTR têm diferentes CS s', mas desta forma devemos criar o nosso CS dinamically, e garantir os ctors cópias de classes sh_ptr para transmitir esta Cs compartilhada. Agora chegamos ao mesmo problema: quem quaranties que este ptr Cs já foi eliminada ou não. Nós podemos ser um pouco mais smarty com bandeiras m_bReleased voláteis por exemplo, mas desta forma não podemos preso as lacunas de segurança entre a verificação da bandeira e usando o Cs compartilhada. Eu não posso ver resolução completamente seguro para este problema. Talvez aquela terrível Cs global seria o mau menor como matar o aplicativo. (Sorry for my Inglês)

Isto não pode ser exatamente o que você quer, mas a documentação boost::atomic fornece um exemplo de como usar um contador atômica com intrusive_ptr. intrusive_ptr é um dos ponteiros inteligentes impulso, ele faz "contagem de referência intruso", o que significa que o contador é "embutido" na-alvo, em vez de fornecer pelo ponteiro inteligente.

Aumento atomic de uso Exemplos:

http://www.boost.org/doc/html/atomic/ usage_examples.html

Na minha opinião, a solução mais fácil é usar um intrusive_ptr com algumas pequenas (mas necessário) modificações.

Eu compartilhei minha implementação abaixo:

http://www.philten.com/boost-smartptr-mt/

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