Bienvenido, les saluda Luis y esta vez les traigo este artículo.
Índice
Arreglando pruebas inestables, exc_bad_access
y bloqueos extraños
Thread Sanitizer, también conocido como TSan
, es una herramienta basada en LLVM
para auditar problemas de subprocesos en su código escrito en lenguaje Swift
y C
.
Se introdujo por primera vez en Xcode 8
y puede ser una gran herramienta para encontrar problemas menos visibles en su código.
En WeTransfer, Thread Sanitizer nos ayudó a resolver pruebas inestables y fallas extrañas que realmente no pudimos identificar con una causa determinada.
Puede que no esté claro qué hace esta herramienta y cómo funciona. Por lo tanto, es hora de sumergirse y explicar cómo puede mejorar su código con Thread Sanitizer.
¿Qué son las carreras de datos?
Antes de sumergirnos en el desinfectante, primero debemos saber qué estamos buscando realmente. Vamos a arreglar algo que se llama carrera de datos.
Las carreras de datos ocurren cuando se accede a la misma memoria desde múltiples subprocesos sin sincronización y al menos un acceso es una escritura. Las carreras de datos pueden provocar varios problemas:
- Comportamiento impredecible.
- Corrupción de memoria.
- Pruebas escamosas.
- Choques extraños.
Como una carrera de datos es impredecible, puede ocurrir de manera inconsistente al probar su aplicación. Es posible que tenga un bloqueo en el inicio que no vuelva a ocurrir la segunda vez que inicie su aplicación.
Si este es el caso, es posible que se trate de una carrera de datos.
Ejemplos de una carrera de datos en Swift
Una vez que sepa qué es una carrera de datos, puede mejorar en la detección de problemas potenciales en su código. Sin embargo, a veces es menos claro que el código podría conducir a una carrera de datos .
Aquí hay algunos ejemplos para mostrarle qué es una carrera de datos.
En el siguiente fragmento de código, dos subprocesos diferentes acceden a la misma propiedad String
:
Como el hilo de fondo está escribiendo en el nombre, tenemos al menos un acceso de escritura. El comportamiento es impredecible, ya que depende de si la instrucción de impresión o escritura se ejecuta como primera.
Este es un ejemplo de una carrera de datos confirmada por Thread Sanitizer.
Una variable diferida retrasa la inicialización de una instancia hasta el momento en que se llama por primera vez. Esto significa que se realizará una escritura de datos en el primer momento en que se acceda a una variable diferida.
Cuando dos subprocesos acceden a esta misma variable diferida por primera vez, puede ocurrir una carrera de datos:
Uso de Thread Sanitizer
para detectar razas de datos
Los ejemplos anteriores nos muestran que una carrera de datos puede ocurrir fácilmente. En pequeños fragmentos de código, es posible que pueda detectar esto, pero se vuelve mucho más difícil tan pronto como su proyecto crece.
Por lo tanto, es hora de que te ayuden a utilizar Thread Sanitizer.
Puede hacer lo mismo con sus esquemas de prueba, que pueden ser una forma eficiente de capturar carreras de datos.
Su aplicación se reconstruirá desde cero una vez que habilite Thread Sanitizer. Se agregará un código alrededor de cada acceso a la memoria para verificar si un determinado acceso participa en una carrera.
El ejemplo de código anterior se vería de la siguiente manera después de que el compilador lo transformara:
func updateName() { DispatchQueue.global().async { self.recordAndCheckWrite(self.name) self.name.append("Antoine van der Lee") } // Executed on the Main Thread self.recordAndCheckWrite(self.name) print(self.name) }
El método recordAndCheckWrite
almacenará una marca de tiempo para cada acceso y cada hilo utilizado por el desinfectante para detectar una carrera de datos.
Thread Sanitizer se puede habilitar desde la configuración del esquema:
Puede hacer lo mismo con sus esquemas de prueba, que pueden ser una forma eficiente de capturar carreras de datos.
Thread Sanitizer viene con algunas restricciones:
- Solo es compatible con macOS de 64 bits y simuladores de iOS y tvOS de 64 bits.
- WatchOS no es compatible.
- No puedes usar TSan en un dispositivo.
Como se cita en el Documentación de Apple, el uso de TSan puede provocar una disminución del rendimiento:
Cómo resolver una carrera de datos
Una vez que sepa qué son las carreras de datos y cómo detectarlas, es hora de escribir una solución para que no vuelvan a ocurrir. Tomando el ejemplo anterior, podríamos escribir una solución de la siguiente manera:
private let lockQueue = DispatchQueue(label: "name.lock.queue") private var name: String = "Antoine van der Lee" func updateNameSync() { DispatchQueue.global().async { self.lockQueue.async { print(self.name) } } // Executed on the Main Thread lockQueue.async { // Executed on the lock queue print(self.name) } } // Prints: // Antoine van der Lee // Antoine van der Lee
Usando una cola de bloqueo, sincronizamos el acceso y nos aseguramos de que sólo un subproceso acceda a la variable de nombre a la vez.
Conclusión
No dude en dejar un comentario si tiene sugerencias o comentarios adicionales.
Gracias por leer este artículo.
Añadir comentario