To make your C#/.NET apps fast and responsive, you must balance the computing time needed to lay out the visuals and handle user interactions with the time needed to process your data and run your business logic. Typically, app developers spread this work across multiple threads: the main or UI thread for all of the user interface-related work, and one or more background threads to compute heavier workloads before sending it to the UI thread for presentation. By offloading heavy work to background threads, the UI thread can remain highly responsive regardless of the size of the workload. But it can be notoriously difficult to write thread-safe, performant, and maintainable multithreaded code that avoids issues like deadlocking and race conditions. Realm aims to simplify this for you.
Importante
Hilos de SynchronizationContext
En esta página, nos referimos al "hilo principal" (o "hilo de interfaz de usuario") y a los "hilos en segundo plano". Para ser más precisos, cualquier mención del hilo principal o de interfaz de usuario se refiere a cualquier hilo con...
Contexto de sincronización, mientras que aquellos hilos sin un SynchronizationContext se consideran hilos de fondo.
Three Rules to Follow
Before exploring Realm's tools for multithreaded apps, you need to understand and follow these three rules:
- No bloquees para leer:
- La arquitectura de Control de Concurrencia Multiversión (MVCC) de Realm elimina la necesidad de bloqueos en las operaciones de lectura. Los valores leídos nunca se corromperán ni se modificarán parcialmente. Se puede leer libremente del mismo archivo de Realm en cualquier hilo sin necesidad de bloqueos ni mutex. Un bloqueo innecesario supondría un cuello de botella en el rendimiento, ya que cada hilo podría tener que esperar su turno antes de leer.
- Avoid synchronous writes on the UI thread:
- You can write to a Realm file from any thread, but there can be only one writer at a time. Synchronous write transactions block each other. So, a synchronous write on the main thread may result in your app appearing unresponsive while it waits for a write on a background thread to complete. To prevent this, the SDK provides the WriteAsync() method. For more information, see Asynchronous Writes.
- Don't pass live objects, collections, or realms to other threads:
- Live objects, collections, and realm instances are thread-confined: that is, they are only valid on the thread on which they were created. Practically speaking, this means you cannot pass live instances to other threads. However, Realm offers several mechanisms for sharing objects across threads.
Communication Across Threads
Para acceder al mismo archivo Realm desde diferentes hilos, se debe instanciar una instancia Realm en cada hilo que requiera acceso. Siempre que especifiques la misma configuración, todas las instancias realm se asignarán al mismo archivo en el disco.
Una de las reglas clave al trabajar con Realm en un entorno multihilo es que los objetos están confinados a un hilo: no puedes acceder a las instancias de un realm, colección u objeto que se originaron en otros hilos. La arquitectura de Control de concurrencia multiversión (MVCC) de Realm significa que podría haber muchas versiones activas de un objeto en cualquier momento. El confinamiento por hilo asegura que todas las instancias en ese hilo sean de la misma versión interna.
When you need to communicate across threads, you have several options depending on your use case:
To modify an object on two threads, query for the object on both threads.
To react to changes made on any thread, use Realm's notifications.
To see changes that happened on another thread in the current thread's realm instance, refresh your realm instance.
Para enviar una vista rápida y de solo lectura del objeto a otros subprocesos, "congele" el objeto.
To keep and share many read-only views of the object in your app, copy the object from the realm.
Refreshing Realms
En el subproceso principal de la Interfaz de Usuario (o en cualquier subproceso con un ciclo de ejecución), Realm actualiza automáticamente los objetos al comienzo de cada iteración del ciclo de ejecución. Entre las iteraciones del ciclo de ejecución, se trabajará en la snapshot; así, los métodos individuales siempre ven una vista coherente y nunca tienen que preocuparse por lo que ocurre en otros hilos.
Al abrir un dominio en un hilo, su estado será la última confirmación de escritura correcta y permanecerá en esa versión hasta que se actualice. Si un hilo no tiene un bucle de ejecución (lo que suele ocurrir en un hilo en segundo plano), se debe llamar manualmente al método Realm.Refresh() para avanzar la transacción al estado más reciente.
Los reinos también se actualizan cuando se confirman transacciones de escritura con Transaction.Commit().
Nota
Failing to refresh Realms on a regular basis could lead to some transaction versions becoming "pinned", preventing Realm from reusing the disk space used by that version, leading to larger file sizes.
Escrituras asincrónicas
El método WriteAsync() proporciona una forma sencilla de descargar el hilo de la interfaz de usuario. Realm iniciará y confirmará la transacción de forma asíncrona, pero el bloque de escritura se ejecutará en el hilo original. Por lo tanto, la espera de cambios es asíncrona, pero la devolución de llamada se ejecuta en el hilo principal. Esto significa que los objetos y las consultas creadas antes del bloque de escritura se pueden usar dentro del bloque sin depender de referencias seguras para subprocesos.
The following code shows two examples of creating an object with 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" }); } );
Nota
Si llamas a WriteAsync() en un hilo en segundo plano, Realm se ejecuta sincrónicamente en el hilo, por lo que es equivalente a llamar a Guardar().
Frozen Objects
Live, thread-confined objects work fine in most cases. However, some apps -- those based on reactive, event stream-based architectures, for example -- need to send immutable copies around to many threads for processing before ultimately ending up on the UI thread. Making a deep copy every time would be expensive, and Realm does not allow live instances to be shared across threads. In this case, you can freeze objects, collections, and realms.
Freezing creates an immutable view of a specific object, collection, or realm that still exists on disk and does not need to be deeply copied when passed around to other threads. You can freely share a frozen object across threads without concern for thread issues.
Al trabajar con objetos congelados, cualquier intento de realizar cualquiera de las siguientes acciones genera una excepción:
Opening a write transaction on a frozen realm.
Modifying a frozen object.
Adding a change listener to a frozen realm, collection, or object.
Una vez congelado, no es posible descongelar un objeto. Puedes usar el método IsFrozen para comprobar si el objeto está congelado. Este método siempre es seguro para subprocesos.
To modify a frozen object, query for it on an unfrozen realm, then modify it.
Frozen objects are not live and do not automatically update. They are effectively snapshots of the object state at the time of freezing.
When you freeze a realm, its child objects also become frozen.
Los objetos congelados permanecen válidos siempre que el realm vivo que los generó permanezca abierto. Por lo tanto, evite cerrar el realm activo hasta que todos los hilos hayan terminado con los objetos congelados. Puede cerrar el realm congelado antes de que se cierre el realm activo.
Importante
Sobre el almacenamiento en caché de objetos congelados
Almacenar en caché demasiados objetos congelados puede tener un impacto negativo en el tamaño del archivo Realm. "Demasiados" depende de tu dispositivo objetivo específico y el tamaño de tus objetos Realm. Si necesitas almacenar en caché un gran número de versiones, considera copiar lo que necesitas fuera de realm.
Realm's Threading Model in Depth
Realm proporciona acceso seguro, rápido, sin bloqueos y concurrente entre hilos con su Control de concurrencia multiversión (MVCC) arquitectura.
Compared and Contrasted with Git
If you are familiar with a distributed version control system like Git, you may already have an intuitive understanding of MVCC. Two fundamental elements of Git are:
Commits, which are atomic writes.
Branches, which are different versions of the commit history.
De forma similar, Realm cuenta con escrituras confirmadas atómicamente en forma de transacciones. Realm también cuenta con múltiples versiones del historial en un momento dado, como las ramas.
A diferencia de Git, que favorece activamente la distribución y la divergencia mediante bifurcación (forking), un realm solo tiene una última versión verdadera en un momento dado y siempre escribe en la cabeza de esa última versión. Realm no puede guardar en una versión anterior. Esto tiene sentido; los datos deben converger en la última versión de la verdad.
Estructura interna
A realm is implemented using a B+ tree data structure. The top-level node represents a version of the realm; child nodes are objects in that version of the realm. The realm has a pointer to its latest version, much like how Git has a pointer to its HEAD commit.
Realm uses a copy-on-write technique to ensure isolation and durability. When you make changes, Realm copies the relevant part of the tree for writing. Realm then commits the changes in two phases:
Realm writes changes to disk and verifies success.
Realm then sets its latest version pointer to point to the newly-written version.
This two-step commit process guarantees that even if the write failed partway, the original version is not corrupted in any way because the changes were made to a copy of the relevant part of the tree. Likewise, the realm's root pointer will point to the original version until the new version is guaranteed to be valid.
Ejemplo
The following diagram illustrates the commit process:

The realm is structured as a tree. The realm has a pointer to its latest version, V1.
Cuando se realiza una escritura, Realm crea una nueva versión V2 basada en V1. Realm crea copias de objetos para su modificación (A 1, C 1), mientras que los enlaces a objetos no modificados siguen apuntando a las versiones originales (B, D).
Después de validar la confirmación, Realm actualiza el puntero a la nueva versión más reciente, V2. A continuación, Realm descarta los nodos antiguos que ya no están conectados al árbol.
Realm utiliza técnicas de copia cero como el mapeo de memoria para gestionar datos. Cuando lees un valor del realm, en realidad estás viendo el valor en el disco físico, no una copia de él. Esta es la base de objetos activos. Esta es también la razón por la que un puntero principal de realm puede configurarse para apuntar a la nueva versión después de que la escritura en disco haya sido validada.
Resumen
Realm permite un código multiproceso simple y seguro cuando se siguen tres reglas:
don't lock to read
Eviteescrituras sincrónicas en el hilo de la interfaz de usuario si escribe en hilos en segundo plano o usa la sincronización del dispositivo
don't pass live objects to other threads.
There is a proper way to share objects across threads for each use case.
In order to see changes made on other threads in your realm instance, you must manually refresh realm instances that do not exist on "loop" threads or that have auto-refresh disabled.
For apps based on reactive, event-stream-based architectures, you can freeze objects, collections, and realms in order to pass shallow copies around efficiently to different threads for processing.
La arquitectura de control de concurrencia multiversión (MVCC) de Realm es similar a la de Git. A diferencia de Git, Realm solo tiene una versión actualizada para cada reino.
Realm realiza compromisos en dos etapas para garantizar el aislamiento y la durabilidad.