DEV Community

GoyesDev
GoyesDev

Posted on

[SC] Combinando Sendable con Locks personalizados

Q — Questions (Preguntas)

Convierte los encabezados en preguntas de estudio:

¿Qué es un mecanismo de bloqueo (lock) y para qué se usa en concurrencia?

Un "lock" es un mecanismo de sincronización de hilos que sirve para proteger el acceso y modificación de un dato. Se usa en concurrencia para que no haya carreras de datos.

¿Cómo protege NSLock el acceso a datos mutables compartidos en la clase BankAccount?

En BankAccount hay una propiedad, balance que se debe proteger con un NSLock: Cada vez que se accede al método se bloquea (.lock()) y justo antes de salir de cada método se libera (.unlock()).

¿Por qué el ejemplo de BankAccount es útil para entender los actores más adelante?

En el contexto de concurrencia de Swift, el actor permite invocar sus métodos con await con lo que consigue serializar los accesos a los datos. Si se va a escribir código nuevo, lo ideal es usar un actor. Sin embargo, en código pre-existente hay que evaluar si basta con usar @unchecked Sendable.

El ejemplo inicial de BankAccount ilustra el acceso a balance de forma serializada con ayuda del NSLock. Usar el lock es una convención. Por otro lado, la implementación con actor también serializa el acceso a los datos (en este caso, balance) con la diferencia de que aquí no hay ninguna convención sino que el lenguaje garantiza este comportamiento.

¿En qué situaciones conviene usar @unchecked Sendable en lugar de refactorizar a un actor?

Si el código es bastante estable, la sincronización del acceso a datos es segura, y si la clase en cuestión tiene muchos clientes y su refactorización puede ser compleja, se puede usar @unchecked Sendable.

¿Qué implica técnicamente migrar una clase con locks a un actor en Swift?

Acceder a todos los métodos requiere usar await, lo que implica que el consumo es asíncrono.

¿Cuáles son las desventajas de migrar de inmediato a un actor en una base de código existente?

Se tendría que modificar a todos los clientes para invocar los métodos del actor con await.

¿Cuándo se recomienda usar Mutex en lugar de NSLock o actores?

Si se quiere sincronizar los hilos y también tener acceso a la clase en cuestión de forma síncrona, entonces se usa Mutex.


R — Recite (Recitar)

Sin mirar el texto, responde con tus propias palabras:

¿Cómo funciona el flujo de bloqueo/desbloqueo en BankAccount?

final class BankAccount: @unchecked Sendable {
  private var balance: Int = 0
  private let lock = NSLock()

  func deposit(amount: Int) {
    lock.lock()
    balance += amount
    lock.unlock()
  }

  func withdraw(amount: Int) {
    lock.lock()
    if balance >= amount {
      balance -= amount
    }
    lock.unlock()
  }

  func getBalance() -> Int {
    lock.lock()
    let currentBalance = balance
    lock.unlock()
    return currentBalance
  }
}
Enter fullscreen mode Exit fullscreen mode
actor BankAccount {
  private var balance: Int = 0

  func deposit(amount: Int) {
    balance += amount
  }

  func withdraw(amount: Int) {
    if balance >= amount {
      balance -= amount
    }
  }

  func getBalance() -> Int {
    return balance
  }
}
Enter fullscreen mode Exit fullscreen mode

¿Qué garantiza @unchecked Sendable y qué responsabilidad transfiere al desarrollador?

¿Qué cambia en el código que consume BankAccount cuando se migra a actor?


R — Review (Revisión)

Preguntas de repaso para consolidar:

¿Cuál es la diferencia entre una clase con NSLock y un actor en términos de acceso concurrente?

¿Por qué @unchecked Sendable es una solución temporal y no definitiva?

¿Qué acción concreta recomienda el autor para no olvidar la deuda técnica al usar @unchecked Sendable?

¿Qué factores debes considerar antes de decidir si migrar a un actor de inmediato?


Bibliografía

Top comments (0)