Menu Docs
Página inicial do Docs
/ /
Atlas Device SDKs
/ /

Segmentação - .NET SDK

Nesta página

  • Três regras a seguir
  • Comunicação entre threads
  • Atualização de Realms
  • Gravações assíncronas
  • Objetos congelados
  • Modelo de threading do Realm em detalhes
  • Comparação e contraste com Git
  • Estrutura interna
  • Resumo

Para tornar seus aplicativos C#/.NET rápidos e responsivos, você deve equilibrar o tempo de computação necessário para organizar os visuais e lidar com interações do usuário com o tempo necessário para processar seus dados e executar sua lógica de negócios. Tipicamente, desenvolvedores de aplicativos distribuem esse trabalho em vários threads: o principal ou thread de UI para todo o trabalho relacionado à interface do usuário, e um ou mais threads de background para calcular volumes de trabalho mais pesados antes de enviá-los ao thread de UI para apresentação. Ao transferir trabalhos pesados para threads de background, o thread de UI pode permanecer altamente responsivo, independentemente do tamanho do volume de trabalho. Mas pode ser notoriamente difícil escrever um código multithread seguro, performático e sustentável que evite problemas como deadlocks e condições de corrida. O Realm visa simplificar isso para você.

Importante

Tópicos de SynchronizationContext

Ao longo desta página, nos referimos ao "thread principal" (ou "thread da interface do usuário") e aos "threads de background". Para ser mais preciso, qualquer menção ao thread principal ou da UI refere-se a qualquer thread com um SynchronizationContext, enquanto os threads sem um SynchronizationContext são considerados threads em background.

Antes de explorar as ferramentas do Realm para aplicações de várias threads, entenda e siga estas três regras:

Não trave a leitura:
A arquitetura de controle de concorrência multiversão (MVCC) da Realm elimina a necessidade de bloqueio para operações de leitura. Os valores que você lê nunca serão corrompidos ou em um estado parcialmente modificado. Você pode ler livremente o mesmo arquivo Realm em qualquer thread sem a necessidade de bloqueios ou mutexes. O bloqueio desnecessário seria um gargalo de desempenho, pois cada thread poderia precisar esperar sua vez antes de ler.
Evite escritas síncronas na thread da UI:
Você pode escrever em um Arquivo de Realm de qualquer thread, mas só pode haver um escritor por vez. Transações de escrita síncronas bloqueiam umas às outras. Portanto, uma escrita síncrona na thread principal pode fazer com que seu aplicativo pareça não responsivo enquanto espera que uma escrita em uma thread em background seja concluída. Para evitar isso, o SDK fornece o método WriteAsync() . Para obter mais informações, consulte Gravações assíncronas.
Não passe objetos ativos, coleções ou realms para outras threads:
Objetos ativos, collections e instâncias de Realm são confinados à thread: ou seja, são válidos somente na thread em que foram criados. De forma prática, isso significa que não é possível passar instâncias ativas para outras threads. No entanto, o Realm oferece vários mecanismos para compartilhar objetos entre threads.

Para acessar o mesmo arquivo Realm de diferentes threads, você deve instanciar uma instância de realm em cada thread que precisa de acesso. Desde que você especifique a mesma configuração, todas as instâncias de realm mapearão para o mesmo arquivo no disco.

Uma das principais regras para trabalhar com o Realm em um ambiente de várias threads é que os objetos são confinados pela thread: não é possível acessar as instâncias de um domínio, coleção ou objeto originado em outras threads. A arquitetura de Controle de concorrência multiversão (MVCC) do Realm significa que pode haver muitas versões ativas de um objeto a qualquer momento. O confinamento de threads garante que todas as instâncias nesse thread tenham a mesma versão interna.

Quando há necessidade de comunicação entre threads, você tem várias opções, dependendo do seu caso de uso:

  • Para modificar um objeto em dois threads, consulte o objeto em ambos os threads.

  • Para reagir às alterações feitas em qualquer thread, use as notificaçõesdo Realm's.

  • Para ver as mudanças que aconteceram em outro thread na instância de domínio do thread atual, atualize sua instância de domínio.

  • Para enviar uma visualização rápida e somente leitura do objeto para outros threads, "freeze" o objeto.

  • Para manter e compartilhar muitas visualizações somente para leitura do objeto em sua aplicação, copie o objeto de realm.

No thread principal da UI (ou em qualquer thread com um loop de execução), o Realm atualiza automaticamente os objetos no início de cada iteração do loop de execução. Entre as iterações de loop de execução, você trabalhará no snapshot, de modo que os métodos individuais sempre vejam uma exibição consistente e nunca precisem se preocupar com o que acontece em outros threads.

Quando você abre pela primeira vez um realm em um thread, seu estado será a mais recente confirmação de escrita bem-sucedida e ele permanecerá nessa versão até ser atualizado. Se uma thread não tiver um loop de execução (o que geralmente é o caso em uma thread em background), o método Realm.Refresh() deverá ser chamado manualmente para avançar a transação para o estado mais recente.

Os domínios também são atualizados quando as transações de escrita são confirmadas com Transaction.Commit().

Observação

Não atualizar os Realm regularmente pode fazer com que algumas versões de transação fiquem "fixadas", impedindo que o Realm reutilize o espaço em disco usado por essa versão, levando a tamanhos de arquivo maiores.

O método WriteAsync() fornece uma maneira simples de descarregar o thread da interface do usuário. O Realm iniciará e confirmará a transação de forma assíncrona, mas o bloco de escrita real será executado na thread original. Portanto, a espera por alterações é assíncrona, mas a chamada de resposta é executada na thread principal. Isso significa que objetos e queries criados antes do bloco de gravação podem ser usados dentro do bloco sem depender de referências threadsafe.

O código a seguir mostra dois exemplos de criação de um objeto com AsyncWrite().

var testItem = new Item
{
Name = "Do this thing",
Status = ItemStatus.Open.ToString(),
Assignee = "Aimee"
};
await realm.WriteAsync(() =>
{
realm.Add(testItem);
});
// Or
var testItem2 =
await realm.WriteAsync(() =>
{
return realm.Add<Item>(new Item
{
Name = "Do this thing, too",
Status = ItemStatus.InProgress.ToString(),
Assignee = "Satya"
});
}
);

Observação

Se você chamar WriteAsync() em um thread de background, o Realm será executado de forma síncrona no thread, então é equivalente a chamar Write().

Objeto vivos, confinados ao thread, funcionam bem na maioria dos casos. No entanto, alguns aplicativos -- aqueles baseados em arquiteturas reativas, baseadas em fluxo de eventos, por exemplo -- precisam enviar cópias imutáveis para vários threads para processamento antes de, finalmente, terminarem no thread da UI. Fazer uma cópia profunda toda vez seria caro, e o Realm não permite que instâncias vivas sejam compartilhadas entre threads. Nesse caso, você pode congelar objeto, collection e domínios.

O congelamento cria uma visão imutável de um objeto, collection ou Realm específico que ainda existe no disco e não precisa ser copiado profundamente quando passado para outros segmentos. Você pode compartilhar livremente um objeto congelado entre threads sem se preocupar com problemas de thread.

Quando estiver trabalhando com objetos congelados, uma tentativa de realizar qualquer uma das seguintes ações gera uma exceção:

  • Abrir uma transação de gravação em um realm congelado.

  • Modificar um objeto congelado.

  • Adicionar um ouvinte de alteração a um realm, collection ou objeto congelado.

Depois de congelado, não é possível descongelar um objeto. Você pode utilizar o método IsFrozen para verificar se o objeto está congelado. Este método é sempre seguro para threads.

Para modificar um objeto congelado, faça uma query para ele em um Realm não congelado e, em seguida, modifique-o.

Objetos congelados não estão ativos e não são atualizados automaticamente. Eles são efetivamente snapshots do estado do objeto no momento do congelamento.

Quando você congela um domínio, seus objetos filhos também ficam congelados.

Objetos congelados continuam válidos desde que o domínio ativo que os gerou permaneça aberto. Portanto evite fechar o domínio ativo até que todas as threads tenham terminado com os objetos congelados. Você pode fechar o domínio congelado antes que o domínio ativo seja fechado.

Importante

Sobre o armazenamento em cache de objetos congelados

Armazenar muitos objetos congelados pode ter um impacto negativo no tamanho do arquivo do domínio. "Muitos" depende do dispositivo-alvo específico e do tamanho dos seus objetos domínio. Se você precisar armazenar em cache um grande número de versões, considere copiar o que você precisa fora do domínio.

O Realm fornece acesso seguro, rápido, sem bloqueios e simultâneo em todos os threads com seu MVCC (Multiversion Concurrency Control) arquitetura.

Se você está familiarizado com um sistema de controle de versão distribuído como o Git, talvez já tenha uma compreensão intuitiva do MVCC. Dois elementos fundamentais do Git são:

  • Confirmações, que são escritas atômicas.

  • Ramificações, que são versões diferentes do histórico de confirmações.

Da mesma forma, o Realm tem gravações confirmadas atomicamente na forma de transações. O Realm também tem muitas versões diferentes da história a qualquer momento, como ramificações.

Ao contrário do Git, que suporta ativamente a distribuição e a divergência por meio de forking, um Realm só tem uma versão mais recente verdadeira em um determinado momento e sempre escreve para o head dessa versão mais recente. O Realm não pode escrever em uma versão anterior. Isso faz sentido: seus dados devem convergir para uma versão mais recente da verdade.

Um domínio é implementado usando uma estrutura de dados B+tree.. O nó de alto nível representa uma versão do domínio; nós secundários são objetos nessa versão do domínio. O domínio tem um ponteiro para sua versão mais recente, assim como o Git tem um ponteiro para sua confirmação de CABEÇALHO.

O Realm usa uma técnica de cópia em gravação para garantir isolamento e durabilidade. Quando você faz alterações, o Realm copia a parte relevante da árvore para gravar. Em seguida, Realm confirma as alterações em duas fases:

  • O Realm escreve as alterações no disco e verifica se foi feito.

  • Em seguida, o Realm coloca o ponteiro da versão mais recente apontando para a versão recém-escrita.

Esse processo de confirmação em duas etapas garante que, mesmo que a gravação falhe parcialmente, a versão original não será corrompida de forma alguma, pois as alterações foram feitas em uma cópia da parte relevante da árvore. Da mesma forma, o ponteiro raiz do domínio apontará para a versão original até que a nova versão seja válida.

Exemplo

O diagrama a seguir ilustra o processo de confirmação:

O Realm copia a parte relevante da árvore para gravações e, em seguida, substitui a versão mais recente atualizando um ponteiro.
clique para ampliar
  1. O realm é estruturado como uma árvore. O realm tem um ponteiro para a sua última versão, V1.

  2. Ao escrever, o Realm cria uma nova versão V2 com base na V1. Realm faz cópias de objetos para modificação (A 1 , C 1), enquanto links para objetos não modificados continuam a apontar para as versões originais (B, D).

  3. Após validar o commit, o Realm atualiza o ponteiro para a nova versão mais recente, V2. O Realm então descarta nós antigos que não estão mais conectados à árvore.

Realm usa técnicas de cópia zero, como mapeamento de memória, para lidar com dados. Quando você lê um valor do realm, você está virtualmente olhando para o valor no disco real, não uma cópia dele. Esta é a base para objetos vivos. É também por isso que um ponteiro de head de realm pode ser definido para apontar para a nova versão depois que a gravação no disco tiver sido validada.

  • O Realm habilita a programação simples e segura de várias threads seguindo três regras:

    • não travar para ler

    • evite escritas síncronas na thread da UI se escrever em threads no background ou usar o Device Sync

    • não passar objetos ativos para outras threads.

  • Há uma maneira adequada de compartilhar objetos entre threads para cada caso de uso.

  • Para ver as alterações feitas em outras threads na sua instância de realm,atualize manualmente as instâncias de realm que não existem nas threads de "loop" ou cuja atualização automática esteja desativada.

  • Para aplicativos baseados em arquiteturas reativas e em fluxo de eventos, você pode congelar objetos, collections e realms para passar cópias superficiais de forma eficiente para diferentes threads para processamento.

  • A arquitetura de controle de concorrência multiversão (MVCC) do Realm é semelhante à do Git. Diferentemente do Git, o Realm possui apenas uma única versão mais recente para cada realm.

  • O Realm faz confirmações em duas etapas para garantir o isolamento e a durabilidade.

Voltar

Excluir