É seguro manipular objetos que criei fora do meu thread se não acessá-los explicitamente no thread que os criou?

StackOverflow https://stackoverflow.com/questions/67154

Pergunta

Estou trabalhando em um software cacau e para manter a GUI responsiva durante uma importação massiva de dados (Core Data), preciso executar a importação fora do thread principal.

É seguro acessar esses objetos mesmo que eu os tenha criado no thread principal sem usar bloqueios se Não acesso explicitamente esses objetos enquanto o thread está em execução.

Foi útil?

Solução

Com Core Data, você deve ter um contexto de objeto gerenciado separado para usar em seu thread de importação, conectado ao mesmo coordenador e armazenamento persistente.Você não pode simplesmente lançar objetos criados em um contexto usado pelo thread principal em outro thread e esperar que funcionem.Além disso, você não pode fazer seu próprio bloqueio para isso;você deve, no mínimo, bloquear o contexto do objeto gerenciado em que os objetos estão, conforme apropriado.Mas se esses objetos estiverem vinculados às suas visualizações e controles, não haverá "ganchos" aos quais você possa adicionar esse bloqueio do contexto.

Não existe almoço grátis.

Ben Trumbull explica algumas das razões pelas quais você precisa usar um contexto separado e por que "apenas ler" não é tão simples ou seguro quanto você imagina, em esta ótima postagem do final de 2004 na lista webobjects-dev.(Todo o tópico é ótimo.) Ele está discutindo o Enterprise Objects Framework e o WebObjects, mas seu conselho também se aplica totalmente ao Core Data.Basta substituir "EC" por "NSManagedObjectContext" e "EOF" por "Core Data" no centro de sua mensagem.

A solução para o problema de compartilhar dados entre threads nos dados principais, como a estrutura de objetos corporativos antes dele, é "não". Se você pensou mais sobre isso e realmente, honestamente, precisa compartilhar dados entre threads, a solução é manter os gráficos de objetos independentes em contextos isolados por threads e usar as informações na notificação de salvamento de um contexto para dizer o outro contexto o que se retirar. -[NSManagedObjectContext refreshObject:mergeChanges:] foi projetado especificamente para suportar esse uso.

Outras dicas

Eu acredito que isso é não seguro para fazer com NSManagedObjects (ou subclasses) que são gerenciados por um CoreData NSManagedObjectContext.Em geral, o CoreData pode fazer muitas coisas complicadas com o estado dos objetos gerenciados, incluindo disparar falhas relacionadas a esses objetos em threads separados.Em particular, [NSManagedObject initWithEntity:insertIntoManagedObjectContext:] (o inicializador designado para NSManagedObjects a partir do OS X 10.5) não garante que o objeto retornado seja seguro para passar para outro thread.

O uso do CoreData com vários threads está bem documentado no site da Apple site de desenvolvimento.

O objetivo do uso de bloqueios é garantir que dois threads não tentem acessar o mesmo recurso.Se você puder garantir isso através de algum outro mecanismo, vá em frente.

Mesmo que seja seguro, não é a prática recomendada usar dados compartilhados entre threads sem sincronizar o acesso a esses campos.Não importa qual thread criou o objeto, mas sim se mais de uma linha de execução (thread/processo) está acessando o objeto ao mesmo tempo, pois isso pode levar à inconsistência de dados.

Se você tiver certeza absoluta de que apenas um thread acessará esse objeto, será seguro não sincronizar o acesso.Mesmo assim, prefiro colocar sincronização em meu código agora do que esperar até mais tarde, quando uma alteração no aplicativo colocar um segundo thread compartilhando os mesmos dados sem me preocupar com a sincronização do acesso.

Sim, é seguro.Um padrão bastante comum é criar um objeto e adicioná-lo a uma fila ou alguma outra coleção.Um segundo thread de “consumidor” pega itens da fila e faz algo com eles.Aqui, você precisaria sincronizar a fila, mas não os objetos adicionados à fila.

NÃO é uma boa ideia apenas sincronizar tudo e torcer pelo melhor.Você precisará pensar com muito cuidado sobre seu design e exatamente quais fios podem atuar em seus objetos.

Duas coisas a considerar são:

  • Você deve ser capaz de garantir que o objeto seja totalmente criado e inicializado antes de ser disponibilizado para outros threads.
  • Deve haver algum mecanismo pelo qual o thread principal (GUI) detecte que os dados foram carregados e que está tudo bem.Para ser thread-safe, isso inevitavelmente envolverá algum tipo de bloqueio.

Sim, você pode fazer isso, será seguro

...até que o segundo programador apareça e não entenda as mesmas suposições que você fez.É provável que esse segundo (ou terceiro, quarto, quinto, ...) programador comece a usar o objeto de maneira não segura (no thread do criador).Os problemas causados ​​podem ser muito sutis e difíceis de detectar.Só por esse motivo, e porque é tão tentador usar esse objeto em vários threads, eu tornaria o thread do objeto seguro.

Para esclarecer, (obrigado a quem deixou comentários):

Por "thread safe", quero dizer desenvolver programaticamente um esquema para evitar problemas de threading.Não quero dizer necessariamente criar um esquema de bloqueio em torno do seu objeto.Você pode encontrar uma maneira em seu idioma de tornar ilegal (ou muito difícil) o uso do objeto no thread do criador.Por exemplo, limitando o escopo, no thread criador, ao bloco de código que cria o objeto.Uma vez criado, passe o objeto para o thread do usuário, certificando-se de que o thread do criador não tenha mais uma referência a ele.

Por exemplo, em C++

void CreateObject()
{
    Object* sharedObj = new Object();
    PassObjectToUsingThread( sharedObj); // this function would be system dependent
}

Então, no seu thread de criação, você não tem mais acesso ao objeto, após sua criação, a responsabilidade é passada para o thread que o utiliza.

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