Simultaneidade Swift - Swift SDK
Nesta página
O sistema de concorrência do Swift oferece suporte integrado para escrever código assíncrono e paralelo de forma estruturada. Para obter uma visão geral detalhada do sistema de concorrência do Swift , consulte o tópico Concorrência da linguagem de programação Swift .
Embora as considerações nesta página se apliquem amplamente ao uso do domínio com recursos de simultaneidade Swift, o domínio Swift SDK versão 10.39.0 adiciona suporte para o uso do domínio com Swift Actors. Você pode usar o Realm isolado para um único ator ou usar o Realm entre atores.
O suporte a atores da Realm simplifica o uso do Realm em um contexto de ator principal e de ator em segundo plano e substitui grande parte dos conselhos nesta página sobre considerações de simultaneidade. Para obter mais informações, consulte Usar Realm com atores - Swift SDK.
Advertências sobre simultaneidade de Realm
Ao implementar recursos de simultaneidade em seu aplicativo, considere esta ressalva sobre o modelo de threading do Realm e os comportamentos de threading de simultaneidade do Swift.
Suspendendo a execução com Await
Em qualquer lugar que você use a palavra-chave Swift await
marca um possível apontar de suspensão na execução do seu código. Com o Swift 5.7, assim que seu código for suspenso, o código subsequente poderá não ser executado na mesma thread. Isso significa que, em qualquer lugar que você use await
em seu código, o código subsequente pode ser executado em uma thread diferente do código que o precede ou segue.
Isso é inerentemente incompatível com o paradigma de objetos vivos do Realm. Objetos ativos, coleções e instâncias de domínio são confinados na thread: ou seja, são válidos somente na thread em que foram criadas. 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. Esses mecanismos geralmente exigem que seu código faça algum tratamento explícito para passar dados com segurança entre threads.
Você pode usar alguns desses mecanismos, como objetos congelados ou o ThreadSafeReference, para usar com segurança objetos de Realm e instâncias em threads com a palavra-chave await
. Você também pode evitar problemas relacionados ao threading marcando qualquer código de Realm assíncrono com @MainActor
para garantir que suas aplicações sempre executem esse código no thread principal.
Como regra geral, lembre-se de que o uso do Realm em um contexto await
sem incorporar a proteção de threading pode gerar um comportamento inconsistente. Às vezes, o código pode ser bem-sucedido. Em outros casos, ele pode gerar um erro relacionado à gravação em um thread incorreto.
API assíncronas/de espera
Muitas API do Realm Swift que envolvem o trabalho com um aplicativo Atlas App Services ou um domínio sincronizado são compatíveis com a sintaxe async/await do Swift. Por exemplo, confira:
Se você tiver solicitações de recursos específicos relacionados às API async/await do Swift, consulte o mecanismo de feedback do MongoDB para Realm. A equipe do Swift SDK planeja continuar a desenvolver recursos relacionados à simultaneidade com base no feedback da comunidade e na evolução da simultaneidade do Swift.
Executar escrita em segundo plano
Um caso de uso comumente solicitado para código assíncrono é executar operações de escrita em segundo plano sem bloquear a thread principal.
O Realm possui duas API que permitem realizar gravações assíncronas:
A API writeAsync() permite realizar gravações assíncronas usando manipuladores de conclusão Swift.
A API asyncWrite() permite executar gravações assíncronas usando a sintaxe Swift async/await.
Ambas as APIs permitem que você adicione, atualize ou exclua objetos em segundo plano sem usar objetos congelados ou passar uma referência segura de segmento.
Com a API writeAsync()
, a espera para obter o bloqueio de gravação e a confirmação de uma transação ocorrem em segundo plano. O bloco de gravação em si é executado no tópico de chamada. Isso proporciona segurança de thread sem exigir que você manipule manualmente objetos congelados ou passe referências entre threads.
No entanto, embora o próprio bloco de gravação seja executado, isso bloqueia novas transações no tópico de chamada. Isso significa que uma gravação grande usando a API writeAsync()
pode bloquear gravações pequenas e rápidas durante a execução.
A API do asyncWrite()
suspende a tarefa de chamada enquanto espera a sua vez de escrever em vez de bloquear a conversa. Além disso, a E/S real para escrever dados em disco é feita por uma thread de trabalho em background. Para escritas pequenas, usar essa função na thread principal pode bloquear a thread principal por menos tempo do que encaminhar manualmente a escrita para uma thread em background.
Para obter mais informações, incluindo exemplos de código, consulte: Execute uma escrita em background.
Tarefas e grupos de tarefas
A Swift Concurrency fornece APIs para gerenciar Tarefas e Grupos de tarefas. A documentação de concorrência rápida define uma tarefa como uma unidade de trabalho que pode ser executada de forma assíncrona como parte do seu programa. A tarefa permite definir especificamente uma unidade de trabalho assíncrono. O grupo de tarefas permite definir uma coleção de tarefas a serem executadas como uma unidade no grupo de tarefas pai.
Tarefas e grupos de tarefas oferecem a capacidade de transferir o thread para outros trabalhos importantes ou cancelar uma tarefa de longa duração que pode estar bloqueando outras operações. Para obter esses benefícios, você pode ficar tentado a usar Tarefas e Grupos de Tarefas para managed gravações no reino em segundo plano.
No entanto, as restrições de thread confinadas descritas em Suspendendo a execução com Await acima se aplicam no contexto Tarefa. Se sua Tarefa contiver await
pontos, o código subsequente poderá ser executado ou retomado em uma thread diferente e violar o confinamento de thread do Realm.
Você deve anotar as funções que você executa em um contexto de tarefa com @MainActor
para garantir que o código que acessa o Realm seja executado somente no thread principal. Isso nega alguns dos benefícios de usar Tarefas e pode média que essa não é uma boa opção de design para aplicativos que usam o Realm, a menos que você esteja usando Tarefas apenas para atividades de rede, como gerenciar usuários.
Isolamento de atores
Dica
Veja também: Use Realm com Swift Actors
As informações nesta seção são aplicáveis às versões do Realm SDK anteriores à 10.39.0. A partir do Realm Swift SDK versão 10.39.0 e mais recente, o SDK oferece suporte ao uso do Realm com Swift Actors e à funcionalidade assíncrona relacionada.
Para obter mais informações, consulte Usar Realm com atores - Swift SDK.
O isolamento do ator fornece a percepção de confinar o acesso ao Realm a um ator dedicado e, portanto, parece uma maneira segura de manage o access ao Realm em um contexto asíncrono.
No entanto, o uso do Realm em uma função assíncrona que não seja@MainActor
atualmente não é suportado.
No Swift 5.6, isso geralmente funcionava por coincidência. A execução após um await
continuaria em qualquer thread em que o item esperado fosse executado. Usar await Realm()
em uma função assíncrona resultaria na execução do código seguinte na thread principal até sua próxima chamada para uma função isolada do ator.
Em vez disso, o Swift 5.7 alterna as threads sempre que muda os contextos de isolamento do ator. Em vez disso, uma função assíncrona isolada sempre é executada em uma thread de fundo.
Se você tiver código que utiliza await Realm()
e trabalha no 5.6, marcar a função como @MainActor
fará com que ela funcione com Swift 5.7. Ele funcionará como funcionou - involuntariamente - no 5.6.
Erros relacionados ao código de simultaneidade
Na maioria das vezes, o erro que você vê relacionado ao acesso ao Realm por meio de código de concorrência é Realm accessed from incorrect thread.
Isso se deve aos problemas de isolamento de thread descritos nesta página.
Para evitar problemas relacionados a segmentações em códigos que usam recursos de simultaneidade do Swift:
Atualize para uma versão do domínio Swift SDK que ofereça suporte a domínios isolados do ator e use isso como uma alternativa para gerenciar manualmente o threading. Para obter mais informações, consulte Usar Realm com atores - Swift SDK.
Não altere os contextos de execução ao acessar um Realm. Se você abrir um Realm na thread principal para fornecer dados para sua UI, anote as funções subsequentes onde você acessa o Realm de forma assíncrona com o
@MainActor
para garantir que ele sempre seja executado na thread principal. Lembre-se de queawait
marca um ponto de suspensão que pode mudar para uma thread diferente.Os aplicativos que não usam domínios isolados por ator podem usar a API
writeAsync
para executar uma gravação em segundo plano. Isso gerencia o acesso ao domínio de maneira segura para threads, sem exigir que você escreva código especializado para fazer isso sozinho. Esta é uma API especial que terceiriza aspectos do processo de gravação - onde for seguro fazê-lo - para execução em um contexto assíncrono. A menos que você esteja gravando em um domínio isolado por ator, você não usa este método com a sintaxeasync/await
do Swift. Use este método de forma síncrona em seu código. Como alternativa, você pode usar a APIasyncWrite
com a sintaxeasync/await
do Swift ao aguardar gravações em regiões assíncronas.Se quiser escrever explicitamente um código de simultaneidade que não seja isolado por atores, em que o acesso a um reino seja feito de forma segura para threads, você poderá passar explicitamente instâncias entre threads, quando aplicável, para evitar falhas relacionadas a threads. Isso requer um bom entendimento do modelo de threading do Realm, além de estar atento aos comportamentos de threading de simultaneidade do Swift.
Tipos Enviáveis, Não Enviáveis e Confinados a Threads
A API pública do SDK do Realm Swift contém tipos que se enquadram em três categorias amplas:
Enviável
Não pode ser enviado e não está confinado a threads
Confinado por thread
Você pode compartilhar tipos que não são Sendable e que não são confinados por thread entre threads, mas deve sincronizá-los.
Os tipos confinados por thread, a menos que congelados, estão confinados a um contexto de isolamento. Você não pode passá-los entre esses contextos, mesmo com sincronização.
Enviável | Non-Sendable | Confinada à thread |
---|---|---|
AnyBSON | RLMAppConfiguration | AnyRealmCollection |
AsyncOpen | RLMFindOneAndModifyOptions | AnyRealmValue |
AsyncOpenSubscription | RLMFindOptions | Lista |
RLMAPIKeyAuth | RLMNetworkTransport | Map |
RLMApp | RLMRequest | MutableSet |
RLMAsyncOpenTask | RLMResponse | Projeção |
RLMChangeStream | RLMSyncConfiguration | RLMArray |
RLMCompensatingWriteInfo | RLMSyncTimeoutOptions | RLMChangeStream |
RLMCredentials | RLMDictionary | |
RLMDecimal128 | RLMDictionaryChange | |
RLMEmailPasswordAuth | RLMEmbeddedObject | |
RLMMaxKey | RLMLinkingObjects | |
RLM Minkey | Objeto RLM | |
Cliente RLMMongoClient | RLMPropertyChange | |
RLMMongoCollection | RLMRealm | |
RLMMongoDatabase | RLMResults | |
RLMObjectId | RLMSection | |
RLMObjectSchema | RLMMongoClient | |
RLMProgressNotification | RLMSectionedResultsChangeset | |
RLMProgressNotificationToken | RLMSet | |
RLMProperty | RLMSyncSubscription | |
RLMPropertyDescriptor | RLMSyncSubscriptionSet | |
RLMProviderClient | RealmOptional | |
RLMPushClient | RealmProperty | |
RLMSchema | ||
RLMSortDescriptor | ||
RLMSyncErrorActionToken | ||
RLMSyncManager | ||
RLMSyncSession | ||
RLMThreadSafeReference | ||
RLMUpdateResult | ||
RLMUser | ||
RLMUserAPIKey | ||
RLMUserIdentity | ||
RLMUserProfile | ||
ThreadSafe |